|
| 1 | +# RTFM: How Reading the Manual Would Have Saved Hours of Automation Work |
| 2 | + |
| 3 | +**Date**: January 26, 2026 |
| 4 | +**Author**: Claude + Human collaboration |
| 5 | +**Tags**: rtfm, automation, dhcp, home-assistant, frigate, lessons-learned |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## The Problem I Thought I Had |
| 10 | + |
| 11 | +Every Sunday morning, my Reolink cameras reboot for scheduled maintenance. DHCP assigns them new IPs. Frigate loses the feeds. I wake up to dead cameras. |
| 12 | + |
| 13 | +"The AT&T router doesn't support persistent DHCP reservations," I told myself. "I've set them up before and they disappeared after a router reboot." |
| 14 | + |
| 15 | +So I built a solution. |
| 16 | + |
| 17 | +## The Over-Engineered Solution |
| 18 | + |
| 19 | +Over several hours, I created: |
| 20 | + |
| 21 | +1. **A Python script** that runs nmap scans to discover cameras by MAC address |
| 22 | +2. **A Home Assistant sensor** that polls the script every 5 minutes |
| 23 | +3. **A state-based automation** that detects IP changes |
| 24 | +4. **A Kubernetes webhook** (Flask app) that patches Frigate's ConfigMap |
| 25 | +5. **A GitHub Action** to build and push the webhook container |
| 26 | +6. **Traefik IngressRoute** to expose the webhook |
| 27 | +7. **Reloader** to auto-restart Frigate when the ConfigMap changes |
| 28 | + |
| 29 | +``` |
| 30 | +Camera reboot → DHCP assigns new IP → nmap detects → HA sensor updates → |
| 31 | +automation triggers → REST call to webhook → ConfigMap patched → |
| 32 | +Reloader restarts Frigate → streams reconnect |
| 33 | +``` |
| 34 | + |
| 35 | +Beautiful. Elegant. Completely unnecessary. |
| 36 | + |
| 37 | +## The Actual Solution |
| 38 | + |
| 39 | +After all that work, I went back to the AT&T router to document the "limitation" for future reference. That's when I actually read the help text: |
| 40 | + |
| 41 | +> **Help** |
| 42 | +> |
| 43 | +> The IP Allocation table lists DHCP clients and IP Allocated clients. You may want to create an IP Allocated client so that it will always get the same IP address. |
| 44 | +> |
| 45 | +> To create an IP Allocated client select the Allocate button next to the client. **The IP Allocation Entry section appears.** Choose a Fixed address for the client and click "Save". |
| 46 | +
|
| 47 | +The IP Allocation Entry section appears. **Below the fold. Where I never scrolled.** |
| 48 | + |
| 49 | +For months, I had been clicking "Allocate," seeing nothing happen (because the form appeared below my viewport), and concluding the feature was broken. |
| 50 | + |
| 51 | +The fix took 30 seconds per device: |
| 52 | +1. Click "Allocate" |
| 53 | +2. **Scroll down** |
| 54 | +3. Select "Private fixed:192.168.1.xxx" |
| 55 | +4. Click "Save" |
| 56 | + |
| 57 | +## What I Actually Built vs What I Needed |
| 58 | + |
| 59 | +| What I Built | Time Spent | What I Needed | |
| 60 | +|--------------|------------|---------------| |
| 61 | +| Python nmap scanner | 30 min | Nothing | |
| 62 | +| HA command_line sensor | 20 min | Nothing | |
| 63 | +| State change automation | 15 min | Nothing | |
| 64 | +| K8s webhook (Flask) | 45 min | Nothing | |
| 65 | +| GitHub Action for container | 15 min | Nothing | |
| 66 | +| Traefik IngressRoute | 10 min | Nothing | |
| 67 | +| Reloader deployment | 20 min | Nothing | |
| 68 | +| Testing & debugging | 60 min | Nothing | |
| 69 | +| **Total** | **~4 hours** | **2 minutes** | |
| 70 | + |
| 71 | +## The Layers of Failure |
| 72 | + |
| 73 | +1. **Didn't scroll** - The form appeared below the viewport |
| 74 | +2. **Assumed it was broken** - "AT&T routers are garbage" confirmation bias |
| 75 | +3. **Didn't re-read the docs** - I "knew" how it worked |
| 76 | +4. **Built around the "limitation"** - Classic engineering instinct |
| 77 | +5. **Didn't question the premise** - Why would enterprise routers not support DHCP reservations? |
| 78 | + |
| 79 | +## The Silver Linings |
| 80 | + |
| 81 | +The automation work wasn't entirely wasted: |
| 82 | + |
| 83 | +- **Reloader** is genuinely useful - any ConfigMap change now auto-restarts affected pods |
| 84 | +- **The nmap discovery script** could be useful for inventory/monitoring |
| 85 | +- **The blog post** I wrote documents a real pattern for dynamic IP handling |
| 86 | +- **I learned** more about HA state triggers vs attribute triggers |
| 87 | + |
| 88 | +And the auto-sync system is now a backup. If the router allocations ever fail, the automation will catch it. |
| 89 | + |
| 90 | +## Lessons Learned |
| 91 | + |
| 92 | +### 1. Scroll the Entire Page |
| 93 | +Enterprise UIs from 2005 love putting forms below the fold with no visual indication. |
| 94 | + |
| 95 | +### 2. Re-Read the Docs When Stuck |
| 96 | +"I already tried that" often means "I tried something similar once and made assumptions." |
| 97 | + |
| 98 | +### 3. Question Your Premises |
| 99 | +"The router doesn't support X" should prompt: "Are you sure? Did you actually verify this?" |
| 100 | + |
| 101 | +### 4. Simple Solutions First |
| 102 | +Before building a distributed system to work around a limitation, verify the limitation exists. |
| 103 | + |
| 104 | +### 5. Document Anyway |
| 105 | +Even though I didn't need the automation, the IP assignments are now documented with MAC addresses for when I replace the router. |
| 106 | + |
| 107 | +## The Final State |
| 108 | + |
| 109 | +``` |
| 110 | +AT&T Router IP Allocation: |
| 111 | +├── 192.168.1.107 - TPLINK-WEBCAM (Fixed) |
| 112 | +├── 192.168.1.137 - Hall camera (Fixed) |
| 113 | +├── 192.168.1.138 - Living camera (Fixed) |
| 114 | +├── 192.168.1.124 - homeassistant (Fixed) |
| 115 | +├── 192.168.1.131 - OPNsense (Fixed) |
| 116 | +└── 192.168.1.110 - cloudflared (Fixed) |
| 117 | +
|
| 118 | +Automation status: Still deployed, hopefully never triggers |
| 119 | +``` |
| 120 | + |
| 121 | +## Conclusion |
| 122 | + |
| 123 | +RTFM isn't just for others. It's for me. It's for you. It's for everyone who "already knows" how something works. |
| 124 | + |
| 125 | +The most dangerous phrase in engineering isn't "I don't know how to do this." It's "I already tried that." |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +*Time spent writing this blog post: 15 minutes* |
| 130 | +*Time I would have saved by scrolling down: 4 hours* |
0 commit comments