<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://liberatedbread.com/feed/devices.xml" rel="self" type="application/atom+xml" /><link href="https://liberatedbread.com/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-08-15T20:38:35+00:00</updated><id>https://liberatedbread.com/feed/devices.xml</id><title type="html">Liberated Bread | Devices</title><subtitle>Reclaim your household hardware. Step-by-step guides for liberating abandoned IoT devices from dead cloud services.</subtitle><author><name>Pigs Can Fly Labs LLC</name></author><entry><title type="html">Liberate Your Belkin WeMo Insight Switch</title><link href="https://liberatedbread.com/devices/wemo-switch/" rel="alternate" type="text/html" title="Liberate Your Belkin WeMo Insight Switch" /><published>2026-08-15T20:38:35+00:00</published><updated>2026-08-15T20:38:35+00:00</updated><id>https://liberatedbread.com/devices/wemo-switch</id><content type="html" xml:base="https://liberatedbread.com/devices/wemo-switch/"><![CDATA[<!--
  Device metadata bar is rendered by _layouts/device.html from front-matter.
  Content below is the liberation guide body.
-->

<h2 id="what-youre-liberating-from">What You're Liberating From</h2>

<p>In January 2026, Belkin shut down its WeMo cloud servers. The WeMo Insight Switch (model F7C029) — a Wi-Fi smart plug with energy monitoring — stopped responding to the WeMo app and cloud-dependent integrations. But the hardware is fine. The switch still works, still connects to Wi-Fi, and still responds to local UPnP commands. It just needs to be told to stop phoning home and start listening locally.</p>

<p>This guide locks your WeMo switch to LAN-only operation and connects it to Home Assistant via PyWeMo. No cloud. No app. No account.</p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>A Belkin WeMo Insight Switch (F7C029) on firmware <code class="language-plaintext highlighter-rouge">WeMo_WW_2.00.11426.PVT-OWRT-SNS</code></li>
  <li>A router where you can configure firewall rules (most consumer routers can do this)</li>
  <li>Home Assistant (any recent version)</li>
  <li>The switch must already be connected to your Wi-Fi (if it's factory-reset, use the WeMo app one last time to get it on your network, then proceed)</li>
</ul>

<h2 id="step-1-lock-it-down">Step 1: Lock It Down</h2>

<p>The goal is to block the switch from reaching Belkin's servers while allowing local traffic.</p>

<h3 id="block-wan-access-for-the-wemo">Block WAN access for the WeMo</h3>

<p>Log into your router's admin panel and create a firewall rule:</p>

<ol>
  <li>Find your WeMo switch's IP address (check your router's DHCP client list)</li>
  <li>Create an <strong>outbound firewall rule</strong> that blocks all WAN (internet) traffic from that IP</li>
  <li>Make sure the rule allows LAN traffic (otherwise Home Assistant can't reach it)</li>
</ol>

<p>Alternative: if your router supports it, put the WeMo on a separate VLAN with no internet access.</p>

<h3 id="verify-the-block">Verify the block</h3>

<p>After applying the rule, confirm the switch can't reach the internet:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># From a computer on the same network, try to reach Belkin's old API through the switch:</span>
<span class="c"># The switch should no longer respond to cloud pings</span>
</code></pre></div></div>

<p>Your WeMo switch is now LAN-only. It can't phone home, but it's still reachable from within your network.</p>

<h2 id="step-2-adopt-it-locally">Step 2: Adopt It Locally</h2>

<h3 id="add-to-home-assistant">Add to Home Assistant</h3>

<p>Home Assistant has built-in PyWeMo support. Add this to your <code class="language-plaintext highlighter-rouge">configuration.yaml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Home Assistant configuration.yaml</span>
<span class="na">wemo</span><span class="pi">:</span>
  <span class="na">discovery</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">static</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">192.168.1.50</span>   <span class="c1"># Replace with your WeMo's IP address</span>
</code></pre></div></div>

<p>Restart Home Assistant. The WeMo switch should appear automatically as a <code class="language-plaintext highlighter-rouge">switch</code> entity.</p>

<h3 id="verify-entity">Verify entity</h3>

<p>Check that the switch entity works:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Developer Tools → States</span>
<span class="s">switch.wemo_insight_switch</span>   <span class="c1"># Should show current state</span>
</code></pre></div></div>

<p>Toggle it on and off from Home Assistant. It should respond within 1–2 seconds — entirely local, no cloud delay.</p>

<h2 id="step-3-verify">Step 3: Verify</h2>

<h3 id="test-offline-operation">Test offline operation</h3>

<ol>
  <li>Temporarily disconnect your internet (unplug WAN from router)</li>
  <li>Toggle the WeMo switch from Home Assistant</li>
  <li>It should still work — the switch and HA are communicating entirely within your LAN</li>
</ol>

<h3 id="test-energy-monitoring">Test energy monitoring</h3>

<p>The Insight model reports power usage. Check the <code class="language-plaintext highlighter-rouge">sensor.wemo_insight_switch_current_power_w</code> entity in Home Assistant.</p>

<h3 id="reconnect-internet">Reconnect internet</h3>

<p>Plug WAN back in. The firewall rule still blocks the WeMo from phoning home, so nothing changes. Your switch is permanently liberated.</p>

<h2 id="troubleshooting">Troubleshooting</h2>

<h3 id="switch-not-discovered">Switch not discovered</h3>

<ul>
  <li>Double-check the static IP address in <code class="language-plaintext highlighter-rouge">configuration.yaml</code></li>
  <li>Verify the switch is on the same subnet as Home Assistant</li>
  <li>Check your firewall rule isn't blocking LAN traffic between HA and the WeMo</li>
</ul>

<h3 id="switch-appears-but-wont-toggle">Switch appears but won't toggle</h3>

<ul>
  <li>The WeMo may need a power cycle — unplug it for 10 seconds and plug it back in</li>
  <li>Check that the firmware version matches (<code class="language-plaintext highlighter-rouge">WeMo_WW_2.00.11426.PVT-OWRT-SNS</code>)</li>
  <li>If the switch was recently cloud-connected, it may take a few minutes to "give up" on the cloud and start responding locally</li>
</ul>

<h3 id="energy-monitoring-not-showing">Energy monitoring not showing</h3>

<ul>
  <li>The Insight's energy monitoring uses a separate UPnP service — verify your firewall isn't blocking UPnP multicast on the LAN (it shouldn't be)</li>
</ul>

<h2 id="going-further-esp32-hardware-replacement">Going Further: ESP32 Hardware Replacement</h2>

<p>This guide keeps your WeMo switch working with its original firmware. If you want to go further — replace the stock firmware entirely for full local control — an ESP32 hardware replacement is the next step. That involves opening the switch and flashing custom firmware, so it is a separate hardware-liberation guide carrying the full safety block. It has not been published yet — it will appear in the <a href="/devices/">device index</a> when it lands.</p>

<h2 id="protocol-reference">Protocol Reference</h2>

<p>The WeMo Insight Switch communicates via UPnP over Wi-Fi. Full protocol specification:</p>

<ul>
  <li><a href="https://github.com/liberatedbread/liberatedbread-protocol-specs/blob/main/device-specs/devices/wemo-devices.yaml">WeMo Device Spec (liberatedbread-protocol-specs)</a></li>
  <li><a href="https://github.com/liberatedbread/liberatedbread-protocol-specs/blob/main/docs/devices/wemo-devices.md">WeMo Protocol Documentation</a></li>
</ul>

<hr />

<p><em>Written against firmware <code class="language-plaintext highlighter-rouge">WeMo_WW_2.00.11426.PVT-OWRT-SNS</code> and Home Assistant 2026.7, from
Belkin’s UPnP device documentation and the PyWeMo integration.</em></p>]]></content><author><name>Pigs Can Fly Labs LLC</name></author><category term="wemo" /><category term="belkin" /><category term="smart-plug" /><category term="wifi" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Liberate Your Wi-Fi Roomba (2024 and Earlier)</title><link href="https://liberatedbread.com/devices/roomba/" rel="alternate" type="text/html" title="Liberate Your Wi-Fi Roomba (2024 and Earlier)" /><published>2026-08-15T20:38:35+00:00</published><updated>2026-08-15T20:38:35+00:00</updated><id>https://liberatedbread.com/devices/roomba</id><content type="html" xml:base="https://liberatedbread.com/devices/roomba/"><![CDATA[<!--
  Metadata bar and verification status come from _layouts/device.html.

  NOTE ON STEP ORDER: this guide deliberately does NOT follow the template's
  "Step 1: Lock It Down" ordering. Getting the robot's password can go through
  iRobot's servers, so blocking first would mean unblocking to finish. The
  reason is stated in the body rather than left for the reader to discover.
-->

<h2 id="check-which-robot-you-have-first">Check Which Robot You Have First</h2>

<p><strong>This guide is for Wi-Fi Roombas from 2024 or earlier.</strong> Those run a full
local API and this works on them. The 2025 line does not, and no amount of
following these steps will change that — so it is worth thirty seconds before
reading the rest.</p>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>Local control</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>690, 890, 960, 980, e5/e6, i3–i8, j7/j9, s9, Braava jet m6</td>
      <td><strong>Yes</strong> — this guide applies</td>
    </tr>
    <tr>
      <td>Roomba 105, 205, Combo 405 (2025 line)</td>
      <td><strong>No</strong> — local broker removed; connecting to 8883 is refused outright</td>
    </tr>
  </tbody>
</table>

<p>If you have one of the 2025 models, there is nothing on the robot to talk to.
The rest of this page will not help, and it is better to know that now than
after an evening of handshakes that cannot succeed. What you can still do is
<a href="/firewall/">keep it off the internet</a> — the firewall
guide works on any device — but you will lose the vendor app along with the
cloud, because on those models the app IS the cloud.</p>

<h2 id="what-youre-liberating-from">What You're Liberating From</h2>

<p>Older Wi-Fi Roombas already run a full local API. It's an MQTT broker, on the
robot, on TCP 8883 — the same channel the iRobot app uses when the phone is on
the same Wi-Fi. It needs no account, no internet, and no permission. It has
been there since the 980.</p>

<p>So on those robots there's nothing to fix. What there is, is something to
protect.</p>

<p>iRobot filed Chapter 11 in December 2025 and was bought by Picea Robotics in
January 2026. More to the point: the <strong>2025 model line ships with the local
broker removed.</strong> That is the direction of travel, and the delivery mechanism
for applying it to a robot that still has one is a firmware update.</p>

<p>That is what makes this worth doing now rather than eventually. An older robot
has something the new ones don't, and it keeps it only until it is updated.
This guide gets the credentials off the robot, gets the robot off the internet,
and connects it to Home Assistant — in that order, for a reason.</p>

<blockquote>
  <p><strong>This is <a href="https://github.com/koalazak/dorita980">dorita980</a>'s work.</strong>
<a href="https://github.com/koalazak">koalazak</a> reverse-engineered every part of what
follows: the discovery probe, the password handshake, the command vocabulary.
<a href="https://github.com/pschmitt/roombapy">pschmitt/roombapy</a> carried it into
Python and is what Home Assistant actually runs. We wrote a guide around
their work; they did the work.</p>
</blockquote>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>A <strong>Wi-Fi Roomba from 2024 or earlier</strong> — see the table above.</li>
  <li>The robot already on your Wi-Fi (if it's factory-fresh, use the iRobot app
once to get it on the network, then come back).</li>
  <li>A computer with Node.js, <strong>or</strong> the Liberated Bread app. Either can pull the
password.</li>
  <li>A router where you can block one device from the internet — see
<a href="/firewall/">Keep It Off the Internet</a>.</li>
  <li>Home Assistant, if that's where you want it to end up.</li>
</ul>

<h2 id="step-1-get-the-password-first">Step 1: Get the Password First</h2>

<p><strong>Do this before you touch the firewall.</strong> One of the two routes below goes
through iRobot's servers, and if you block the robot first you'll be
temporarily unblocking it to finish this step. The other route is entirely
local, but it doesn't work on every robot, so you may end up needing the first
one anyway.</p>

<p>You are collecting two values:</p>

<ul>
  <li><strong>BLID</strong> — the robot's identity, and the MQTT username</li>
  <li><strong>Password</strong> — a long per-robot secret that looks like
<code class="language-plaintext highlighter-rouge">:1:1486937829:gktkDoYpWaDxCfGh</code></li>
</ul>

<p><strong>Write both down.</strong> The password only changes on a factory reset, and it's
what Home Assistant, dorita980, and everything else will ask you for. The
Liberated Bread app shows you a screen built for exactly this — screenshot it.</p>

<h3 id="route-a-the-home-button-no-account">Route A: the HOME button (no account)</h3>

<p>Works offline, needs nothing from iRobot.</p>

<ol>
  <li>Put the robot <strong>on its dock</strong>, powered on.</li>
  <li><strong>Close the iRobot app</strong> on every phone in the house. The robot serves one
client at a time and the app will hold the slot.</li>
  <li><strong>Hold the HOME button for about two seconds</strong>, until the robot plays a
series of tones. Release.</li>
  <li>Immediately, on a computer on the same network:</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">-g</span> dorita980
get-roomba-password &lt;robot-ip&gt;
</code></pre></div></div>

<p>It prints the BLID and the password. In the Liberated Bread app, this is the
"Hold the HOME button" path in the Roomba adoption wizard — same handshake,
same result, and it retries for you.</p>

<p>If it fails, re-hold the button and try again before assuming it can't work.
j-series firmware is known to drop the first attempt or two.</p>

<h3 id="route-b-your-irobot-account-extraction-only">Route B: your iRobot account (extraction only)</h3>

<p>If the button route won't complete, log in once and read the same values out of
iRobot's API:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>get-roomba-password-cloud &lt;your-irobot-email&gt; &lt;your-irobot-password&gt;
</code></pre></div></div>

<p>This returns every robot on the account with its BLID and password. <strong>That is
all it's for</strong> — nothing about controlling the robot afterwards needs an
account, and the credentials are identical to what Route A would have given
you. The Liberated Bread app offers this too, and never stores your iRobot
account password; it uses it for the one login call and throws it away.</p>

<p>Once you have the credentials, you never need the account again. Which is
convenient, because in the next step you're going to make it unreachable.</p>

<h2 id="step-2-lock-it-down">Step 2: Lock It Down</h2>

<p>Now block the robot from the internet.</p>

<p>The full how-to, for UniFi, MikroTik, OPNsense/pfSense, OpenWrt, Firewalla and
consumer routers, is on <a href="/firewall/">Keep It Off the Internet</a>. The short
version:</p>

<ol>
  <li>Give the robot a <strong>fixed IP</strong> (a DHCP reservation is fine).</li>
  <li>Add a firewall rule blocking that IP from the WAN, <strong>leaving LAN traffic
alone</strong>.</li>
</ol>

<p>What keeps working: everything in this guide. Local MQTT on 8883, discovery on
UDP 5678, all your commands and all your sensors.</p>

<p>What stops: the iRobot app from outside your house (it still works on your
Wi-Fi), cloud-stored schedules, map sync on the i/j/s series, and firmware
updates. That last one is the entire point.</p>

<blockquote>
  <p><strong>Don't try to block only the update server.</strong> Vendors move update traffic
between hosts without announcing it, and a blocklist that's one host short is
a firmware update that arrives anyway. Block outbound wholesale.</p>
</blockquote>

<h2 id="step-3-adopt-it-locally">Step 3: Adopt It Locally</h2>

<blockquote>
  <p><strong>One thing should hold the robot.</strong> It accepts a single local connection at
a time, and a new one evicts the old. Two things talking to it directly means
both take turns being locked out — including your own iRobot app. So pick one
owner and let everything else go through it. That is the thread running
through this whole section.</p>
</blockquote>

<h3 id="home-assistant--start-here">Home Assistant — start here</h3>

<p>If you run Home Assistant, this is the answer, and it stays the answer even if
you also want the phone app.</p>

<p><strong>Settings → Devices &amp; Services → Add Integration → iRobot Roomba and Braava</strong>.
It asks for the host, the BLID and the password from Step 1. That's it —
Home Assistant's <code class="language-plaintext highlighter-rouge">roomba</code> integration is <code class="language-plaintext highlighter-rouge">local_polling</code>, so it talks to the
robot and nothing else.</p>

<p>You get a vacuum entity (start, pause, stop, return to base, locate), plus
battery and status sensors.</p>

<p>Three reasons to make it the owner rather than one option among several:</p>

<ul>
  <li><strong>It settles the one-client problem.</strong> HA holds the connection; everything
else asks HA. Nothing gets evicted.</li>
  <li><strong>It survives the firewall.</strong> If you put the robot on its own VLAN — the
natural end of the <a href="/firewall/">firewall guide</a> — HA
can still be on a network that reaches it, while your phone roams elsewhere.</li>
  <li><strong>It works with old firmware.</strong> Some robots only offer a cipher modern phone
TLS stacks have dropped. HA's Python stack can still negotiate it.</li>
</ul>

<h3 id="liberated-bread-app">Liberated Bread app</h3>

<p>Open the Wi-Fi tab and scan. The robot answers a broadcast probe on UDP 5678,
so it shows up by name without you typing an address. Tap it, run through the
adoption wizard, and the credentials go into your phone's keychain — not into
preferences, not into a file.</p>

<p><strong>If you already have Home Assistant, point the app at it instead</strong> — on a
robot's screen, choose "How to reach this robot" and pick the Home Assistant
entity. You get the same panel, the same buttons and the same readings, but the
commands travel to HA and HA talks to the robot. Two things worth knowing:</p>

<ul>
  <li>The app then needs <strong>no BLID and no password at all</strong> for that robot. HA
holds them. There is nothing on your phone to leak.</li>
  <li>It works for robots the phone <strong>cannot reach</strong> — a separate VLAN, a
guest SSID with client isolation. The panel does not care, because it is not
the thing dialling the robot.</li>
</ul>

<p>Straight-at-the-robot is the right choice when the app is the only thing
driving it. If anything else is, or might be, put that other thing in front.</p>

<h3 id="command-line">Command line</h3>

<p><code class="language-plaintext highlighter-rouge">get-roomba-password</code> above works straight from a global install — npm links
its binary onto your <code class="language-plaintext highlighter-rouge">PATH</code>. Using dorita980 as a <em>library</em> is different:
<code class="language-plaintext highlighter-rouge">require</code> does not look in npm's global directory, so the snippet below sets
<code class="language-plaintext highlighter-rouge">NODE_PATH</code> to it rather than making you install the package a second time.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># One-off, using dorita980 directly</span>
<span class="nv">BLID</span><span class="o">=</span>&lt;blid&gt; <span class="nv">PASSWORD</span><span class="o">=</span>&lt;password&gt; <span class="nv">ROBOT_IP</span><span class="o">=</span>&lt;ip&gt; <span class="se">\</span>
<span class="nv">NODE_PATH</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>npm root <span class="nt">-g</span><span class="si">)</span><span class="s2">"</span> <span class="se">\</span>
  node <span class="nt">-e</span> <span class="s2">"
    const d = require('dorita980');
    const r = new d.Local(process.env.BLID, process.env.PASSWORD, process.env.ROBOT_IP);
    r.on('connect', () =&gt; r.clean().then(() =&gt; r.end()));
  "</span>
</code></pre></div></div>

<blockquote>
  <p><strong>One client at a time.</strong> The robot accepts a single local connection and a
new one evicts the old. If the iRobot app stops working on your Wi-Fi the
moment Home Assistant connects, that's why — and it's why anything
long-running should connect, act, and disconnect rather than hold the socket
open.</p>
</blockquote>

<h3 id="or-put-rest980-in-front-of-it">Or: put rest980 in front of it</h3>

<p><a href="https://github.com/koalazak/rest980">rest980</a> is koalazak's own HTTP wrapper
around dorita980 — same author as the protocol. It holds the robot connection
and answers plain HTTP, so everything else talks to <em>it</em> instead of fighting
over the robot:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-p</span> 3000:3000 <span class="se">\</span>
  <span class="nt">-e</span> <span class="nv">BLID</span><span class="o">=</span>&lt;blid&gt; <span class="nt">-e</span> <span class="nv">PASSWORD</span><span class="o">=</span><span class="s1">'&lt;password&gt;'</span> <span class="nt">-e</span> <span class="nv">ROBOT_IP</span><span class="o">=</span>&lt;ip&gt; <span class="se">\</span>
  koalazak/rest980
</code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">GET /api/local/action/start</code>, <code class="language-plaintext highlighter-rouge">/dock</code>, <code class="language-plaintext highlighter-rouge">/pause</code>, and
<code class="language-plaintext highlighter-rouge">/api/local/info/state</code>. The Liberated Bread app can be pointed at a rest980
address per robot instead of talking to the robot directly.</p>

<p>This is worth doing in two cases:</p>

<ul>
  <li><strong>More than one thing wants the robot.</strong> One client at a time is the rule, so
an app and a Home Assistant both connecting directly will keep evicting each
other. One rest980, everything else pointed at it, and the problem goes away.</li>
  <li><strong>Old firmware the phone can't reach.</strong> If the robot only offers the
<code class="language-plaintext highlighter-rouge">AES128-SHA256</code> cipher (see Troubleshooting below), Node can speak to it and a
phone genuinely cannot. Run rest980 on a computer and the app works again
through it.</li>
</ul>

<p>One gap worth knowing: rest980 publishes no endpoint for <strong>locate</strong>, so the
"make it beep" button isn't available in that mode. Everything else is.</p>

<h2 id="step-4-verify">Step 4: Verify</h2>

<p>The real test: <strong>unplug your internet</strong>. Not the router — the WAN.</p>

<ol>
  <li>With the internet down, send the robot a clean command from Home Assistant
or the app. It should go.</li>
  <li>Send it home. It should dock.</li>
  <li>Check the battery sensor still updates.</li>
</ol>

<p>Then plug the internet back in and confirm your firewall rule is actually doing
something — your router's firewall log should show blocked connection attempts
from the robot's IP. If it shows nothing at all, the rule may be matching the
wrong thing.</p>

<h2 id="troubleshooting">Troubleshooting</h2>

<h3 id="get-roomba-password-returns-nothing-or-an-error"><code class="language-plaintext highlighter-rouge">get-roomba-password</code> returns nothing, or an error</h3>

<p>The robot wasn't in disclosure mode. Re-hold HOME until you hear the tones —
about two seconds, and the tone is the confirmation, not the clock. Make sure
the robot is <strong>on the dock</strong> and <strong>no phone has the iRobot app open</strong>.</p>

<p>If it fails repeatedly on a j7 or j9, keep trying: that firmware resets the
first connection or two before answering. If it never works, use Route B.</p>

<h3 id="the-password-handshake-fails-before-it-starts-with-a-tls-error">The password handshake fails before it starts, with a TLS error</h3>

<p>Older robot firmware only offers a TLS cipher (<code class="language-plaintext highlighter-rouge">AES128-SHA256</code>) that modern TLS
libraries have retired. Node.js hits this too, which is why dorita980 ships the
<code class="language-plaintext highlighter-rouge">ROBOT_CIPHERS</code> and <code class="language-plaintext highlighter-rouge">ROBOT_TLS_LEGACY</code> environment variables:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ROBOT_TLS_LEGACY</span><span class="o">=</span>1 <span class="nv">ROBOT_CIPHERS</span><span class="o">=</span>AES128-SHA256 get-roomba-password &lt;robot-ip&gt;
</code></pre></div></div>

<p>The Liberated Bread app <strong>cannot</strong> work around this directly — the phone TLS
stack it uses doesn't offer that cipher and gives no way to ask for it. If the
app tells you the robot needs a legacy cipher, that's honest, not a bug. Two
things do work:</p>

<ul>
  <li>Pull the password with dorita980 or roombapy on a computer, then paste it
into the app.</li>
  <li>Run <a href="#or-put-rest980-in-front-of-it">rest980</a> on that computer and point the
app at it. Node selects the old cipher happily, so the robot becomes
reachable again — through the server rather than directly.</li>
</ul>

<h3 id="home-assistant-connects-then-drops">Home Assistant connects, then drops</h3>

<p>Something else took the connection slot. Close the iRobot app, and check you
don't have a second integration or a <code class="language-plaintext highlighter-rouge">rest980</code> container pointed at the same
robot.</p>

<h3 id="the-robot-vanished-from-discovery-after-i-set-up-vlans">The robot vanished from discovery after I set up VLANs</h3>

<p>Discovery is a UDP <strong>broadcast</strong> on port 5678, and broadcast doesn't cross
VLANs. Enabling mDNS reflection doesn't help — that's a different protocol.
Either keep whatever is controlling the robot on the same VLAN, or skip
discovery and configure it by its fixed IP, which works fine.</p>

<h3 id="it-worked-then-stopped-after-a-factory-reset">It worked, then stopped after a factory reset</h3>

<p>A factory reset mints a <strong>new</strong> password. Go back to Step 1.</p>

<h2 id="going-further">Going Further</h2>

<ul>
  <li><strong>Don't factory reset it casually.</strong> Reset changes the password and, on the
mapping models, loses your maps.</li>
  <li><strong>Keep the credentials somewhere you'll find them.</strong> They outlive the app,
the phone, and quite possibly the company.</li>
  <li><strong>Buying another one?</strong> Used 2024-and-earlier robots are cheap and have a
local API. The new ones don't. That asymmetry is unlikely to improve.</li>
  <li><strong>Rooms and maps</strong> aren't covered here. dorita980 can drive per-room cleaning
on the i/j/s series, but the map identifiers come out of the vendor app —
see its README if that's what you're after.</li>
</ul>

<h2 id="protocol-reference">Protocol Reference</h2>

<ul>
  <li>Machine-readable spec:
<a href="https://github.com/liberatedbread/liberatedbread-protocol-specs/blob/main/device-specs/devices/irobot-roomba.yaml"><code class="language-plaintext highlighter-rouge">device-specs/devices/irobot-roomba.yaml</code></a></li>
  <li>Protocol documentation:
<a href="https://github.com/liberatedbread/liberatedbread-protocol-specs/blob/main/docs/devices/irobot-roomba.md"><code class="language-plaintext highlighter-rouge">docs/devices/irobot-roomba.md</code></a></li>
  <li><a href="https://github.com/koalazak/dorita980">koalazak/dorita980</a> — the reference
implementation, and the source of everything above</li>
  <li><a href="https://github.com/koalazak/rest980">koalazak/rest980</a> — a REST API over it</li>
  <li><a href="https://github.com/pschmitt/roombapy">pschmitt/roombapy</a> — the Python
implementation behind Home Assistant</li>
  <li><a href="https://www.home-assistant.io/integrations/roomba/">Home Assistant Roomba integration</a></li>
</ul>

<hr />

<p><em>Written against firmware 2.x and 3.x and Home Assistant 2026.7, from
<a href="https://github.com/koalazak/dorita980">koalazak/dorita980</a> and
<a href="https://github.com/pschmitt/roombapy">pschmitt/roombapy</a> — the projects that
worked this protocol out. Not yet run end to end on a robot by us.</em></p>]]></content><author><name>Pigs Can Fly Labs LLC</name></author><category term="irobot" /><category term="roomba" /><category term="vacuum" /><category term="wifi" /><category term="mqtt" /><category term="dorita980" /><summary type="html"><![CDATA[&lt;!– Metadata bar and verification status come from _layouts/device.html.]]></summary></entry><entry><title type="html">Replace Your Eufy Indoor Cam Mount (3D Printed)</title><link href="https://liberatedbread.com/devices/eufy-mount/" rel="alternate" type="text/html" title="Replace Your Eufy Indoor Cam Mount (3D Printed)" /><published>2026-08-15T20:38:35+00:00</published><updated>2026-08-15T20:38:35+00:00</updated><id>https://liberatedbread.com/devices/eufy-mount</id><content type="html" xml:base="https://liberatedbread.com/devices/eufy-mount/"><![CDATA[<!--
  Device metadata bar is rendered by _layouts/device.html from front-matter.
  Content below is the liberation guide body.
-->

<h2 id="what-youre-building">What You're Building</h2>

<p>The stock Eufy Indoor Cam mount is flimsy, and a common complaint is that the pan-and-tilt mechanism works loose over a few months until the camera sags. This guide walks you through replacing it with a 3D-printed articulating wall mount — more stable, more adjustable, and entirely under your control.</p>

<p>This is a physical modification. No firmware changes, no software liberation — just better hardware.</p>

<h2 id="parts-list">Parts List</h2>

<table>
  <thead>
    <tr>
      <th>Part</th>
      <th>Quantity</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>PLA filament</td>
      <td>~15g</td>
      <td>For the wall mount body</td>
    </tr>
    <tr>
      <td>PETG filament</td>
      <td>~5g</td>
      <td>For the ball joint (more flexible, won't creep under load)</td>
    </tr>
    <tr>
      <td>M3×12mm screws</td>
      <td>2</td>
      <td>Stainless recommended</td>
    </tr>
    <tr>
      <td>M3 nuts</td>
      <td>2</td>
      <td>Nylon lock nuts preferred</td>
    </tr>
    <tr>
      <td>Wall anchors (drywall)</td>
      <td>2</td>
      <td>Standard #6 anchors</td>
    </tr>
    <tr>
      <td>Wood screws</td>
      <td>2</td>
      <td>#6 × 1"</td>
    </tr>
  </tbody>
</table>

<p><strong>Sourcing:</strong> Filament and hardware are available from any 3D printing supplier. No affiliate links yet — we'll add them when we get our Associates account set up.</p>

<h2 id="3d-printed-parts">3D Printed Parts</h2>

<div class="space-y-4 my-6">

  <div class="flex flex-wrap items-center justify-between gap-3 bg-bread-base-dark border border-bread-steel/30 rounded-lg p-4">
    <div>
      <span class="font-mono text-sm text-bread-accent">wall-mount-v2.stl</span>
      <p class="text-sm text-bread-khaki/70 m-0">Articulating wall mount (PLA, 20% infill)</p>
    </div>
    <a href="https://github.com/liberatedbread/liberatedbread-3d-files/raw/main/files/eufy-camera/wall-mount-v2.stl" download="" class="px-3 py-1 text-sm rounded bg-bread-accent/20 text-bread-accent hover:bg-bread-accent/35 no-underline whitespace-nowrap transition-colors">
      <span aria-hidden="true">&darr;</span> Download wall-mount-v2.stl
    </a>
  </div>

  <div class="flex flex-wrap items-center justify-between gap-3 bg-bread-base-dark border border-bread-steel/30 rounded-lg p-4">
    <div>
      <span class="font-mono text-sm text-bread-accent">ball-joint.stl</span>
      <p class="text-sm text-bread-khaki/70 m-0">Ball joint connector (PETG, 50% infill)</p>
    </div>
    <a href="https://github.com/liberatedbread/liberatedbread-3d-files/raw/main/files/eufy-camera/ball-joint.stl" download="" class="px-3 py-1 text-sm rounded bg-bread-accent/20 text-bread-accent hover:bg-bread-accent/35 no-underline whitespace-nowrap transition-colors">
      <span aria-hidden="true">&darr;</span> Download ball-joint.stl
    </a>
  </div>

</div>

<p><strong>Print settings:</strong></p>
<ul>
  <li><strong>Wall mount:</strong> PLA, 0.2mm layers, 20% infill, 3 perimeters, no supports needed</li>
  <li><strong>Ball joint:</strong> PETG, 0.15mm layers, 50% infill, 4 perimeters. PETG is essential here — PLA will creep under the constant load and the joint will loosen</li>
</ul>

<h2 id="step-0-de-energize--unplug">Step 0: De-energize / Unplug</h2>

<p>The Eufy Indoor Cam is low-voltage (5V USB), so there's no mains voltage risk inside the camera itself. But you're drilling into your wall — <strong>check for electrical wiring behind the mounting location</strong> before drilling. If you're unsure, use a stud/wire finder or mount on a stud.</p>

<ol>
  <li>Unplug the camera's USB power cable</li>
  <li>Remove the camera from its existing mount</li>
  <li>Remove the existing mount from the wall (if any)</li>
</ol>

<h2 id="assembly-guide">Assembly Guide</h2>

<h3 id="1-mount-the-wall-plate">1. Mount the wall plate</h3>

<ol>
  <li>Hold the printed wall plate against the wall at your chosen location</li>
  <li>Mark the two screw holes with a pencil</li>
  <li>Drill pilot holes (1/8" for drywall anchors)</li>
  <li>Insert wall anchors and screw the plate to the wall</li>
</ol>

<h3 id="2-assemble-the-ball-joint">2. Assemble the ball joint</h3>

<ol>
  <li>Press the ball joint into the wall plate socket — it should snap in with firm pressure</li>
  <li>Insert the two M3 screws through the ball joint clamp</li>
  <li>Thread the M3 nuts loosely — you'll tighten after positioning</li>
</ol>

<h3 id="3-attach-the-camera">3. Attach the camera</h3>

<ol>
  <li>Slide the camera onto the ball joint mount (it uses the same mounting slot as the stock mount)</li>
  <li>Adjust the camera to your desired angle</li>
  <li>Tighten the M3 nuts to lock the position — don't overtighten, PETG will crack if forced</li>
  <li>Plug in the USB power cable</li>
</ol>

<h3 id="4-test">4. Test</h3>

<p>The camera should hold position firmly. Pan and tilt it manually to verify the ball joint has full range of motion. PETG can settle slightly under a constant load, so check the screws again after 24 hours and retighten a little if needed.</p>

<h2 id="troubleshooting">Troubleshooting</h2>

<h3 id="ball-joint-too-loose">Ball joint too loose</h3>
<ul>
  <li>Reprint at 55% infill (instead of 50%)</li>
  <li>Or add a thin rubber washer between the ball and clamp</li>
</ul>

<h3 id="wall-plate-sags-under-camera-weight">Wall plate sags under camera weight</h3>
<ul>
  <li>Check that you used PLA with 3 perimeters — single-perimeter prints will flex</li>
  <li>If it still sags, reprint with 25% infill and add a small support brace underneath</li>
</ul>

<h3 id="camera-wont-stay-at-desired-angle">Camera won't stay at desired angle</h3>
<ul>
  <li>The M3 screws may have loosened — check and retighten</li>
  <li>If PETG was printed too hot, it may have warped — reprint at 240°C with the part cooling fan at 50%</li>
</ul>

<h2 id="going-further">Going Further</h2>

<p>This guide covers the physical mount. If you want to liberate the camera's software (RTSP streaming without the Eufy cloud), check the Eufy Cam RTSP guide (coming soon). That route locks the camera to LAN-only and streams directly to Home Assistant or Frigate.</p>

<hr />

<p><em>Designed for the Eufy Indoor Cam 2K Pan &amp; Tilt (T8400). The mount geometry and the print
settings come from the part design.</em></p>]]></content><author><name>Pigs Can Fly Labs LLC</name></author><category term="eufy" /><category term="camera" /><category term="mount" /><category term="3d-print" /><summary type="html"><![CDATA[]]></summary></entry></feed>