A Home That Looks After Us, Built with AI
Contents
- First, decide who does what
- Network: cables first, a side router for the gaps
- The wiring
- J4125: one small box, several jobs
- IPv6: going around the main router
- Devices: check for Mijia first
- How Mijia and Home Assistant split the work
- Automations still have to survive real life
- The washer’s status can’t always be trusted
- Walking in isn’t always coming home
- Media library: send a link, get it in the library
- Tailscale: getting home from outside
- An AI that can check on the house
- House rules for letting AI act
- How it feels after living with it
- Living comfortably matters more than extra features
- References
Before the renovation, I thought a smart home meant turning on lights from my phone and asking a speaker to start the air conditioning. Once we moved in, I found the hard part was never that. It was the small things: say something when the laundry is done; keep quiet when someone is napping; don’t announce “welcome home” again when I’ve only popped downstairs for a parcel.
Take the washer. Getting Xiao Ai, Xiaomi’s voice assistant, to say “the laundry is done” should be simple, right? The first time I set it up, it never made a sound (more on why later). Or “welcome home”: say it whenever someone is at the door? Then fetching a parcel earns you a welcome too. Every sentence the house says hides several conditions, and in the past I would probably have given up after writing two. This time was different. Home Assistant now runs a dozen or so automations like these, and nearly all of them came from me describing what I wanted and an AI writing it. Once I gave it tools to read devices and history, it could look up entities, write the configuration, and when a reminder stayed silent, go back through the history to find out why.
One thing up front: turning on lights, closing valves and making announcements never goes through a large language model. The gateway or Home Assistant runs all of it by rules. The AI’s job was helping me build those rules.
First, decide who does what
As I write this, 117 devices are connected at home, 105 of them Mijia (Xiaomi’s smart home platform). Home Assistant shows 3825 entities, which sounds alarming, but every switch, every temperature reading and every status counts as one entity. I did not buy a few thousand more devices (my wallet is relieved).
With this many devices, the first question is: what goes to Mijia, and what goes to Home Assistant? My rule is simple. Anything that must be fast, and keep working when the internet is down, runs locally on the Mijia gateway: a switch turning on a light, a leak closing the water valve. Anything that crosses brands or depends on many conditions goes to Home Assistant. Remote access all goes through Tailscale.
- Devices1Bedside switchSet to "wireless mode": the button only sends a signal, the lights stay powered
- Mijia gateway2Local ruleRight button, single press → run "Bedroom bright"
- Devices3DownlightsOn. Nothing leaves the house, and HA is not involved
- Devices1Presence lightIts mmWave radar sees someone, and ambient light is below 100 lux
- Mijia gateway2Local ruleTurn on the hallway light and the wall lights
- Home Assistant3Arrival checkThe same "someone is here" goes to HA too: six conditions must hold before it counts as coming home
- Cloud & internet4Mijia cloudSends the generated greeting to the Xiaomi speaker
- Devices5Speaker"Good evening, welcome home! …"
- Devices1Leak sensorAny of the six reports water on the floor or water dripping on top
- Mijia gateway2Local ruleClose the main water valve right away
- Home Assistant3Urgent alertWait 10 seconds, confirm the valve state, jump to the front of the announcement queue
- Cloud & internet4Mijia cloudTurn off do-not-disturb, unmute, volume at least 60%
- Devices5SpeakerRepeats every minute until the floor is dry
- Devices1WasherWash phase changes to "done"
- Cloud & internet2Haier cloudThe state syncs out through Haier's cloud
- Home Assistant3Haier integrationAutomation: only 07:30–22:30, at most once per 30 minutes
- Cloud & internet4Mijia cloudPassed on to the Xiaomi speaker
- Devices5Speaker"The laundry is done"; if nobody visits the laundry balcony within an hour, it says it again
- Devices1Hallway switchRight button, single press
- Mijia gateway2"Away" scene63 devices in one go: lights, fresh-air system and all 6 air conditioners off
- Devices3CameraTurned on at the same time; recordings are backed up to the J4125's disk on a schedule
- Cloud & internet1Muse"Is the fridge door closed? How warm is it at home?"
- Cloud & internet2TailscaleMuse's VM is a device on the tailnet and reaches home directly or through a relay
- Home Assistant3HA APIReads entity states with its own token
- Cloud & internet4MuseTurns the states into a short answer
Frequent, latency-sensitive actions run locally on the Mijia gateway.
Internet downWorks with the internet down and HA offline
One signal can take two paths at once: the gateway turns on the light, HA decides what to say.
Internet downLights still come on; no greeting
Closing the valve runs locally; the voice alert takes a separate path.
Internet downThe valve still closes; no announcement
Brands outside Mijia reach the Mijia devices through HA.
Internet downNo announcement
If a scene can be done inside Mijia, don't route it through HA.
Internet downActions the gateway runs locally still work; the camera and backups need checking on their own
The assistant can check on the house without a public IP at home.
Internet downIf the home internet is down, there is no answer
Network: cables first, a side router for the gaps
If a light doesn’t respond, I can press the switch again. But when dozens of devices drop offline now and then, it gets hard to tell whether an automation is wrong or the network is. So the first thing I did during the renovation was get the cabling right.
The wiring
Every room has Ethernet in the wall, all switched to Cat 6A during the renovation. Wi-Fi comes from APs with wired backhaul, no wireless mesh. Once the walls are closed, changing cables is hard, so think through the outlet positions and cable runs twice.
- Incoming line: the fiber modem dials the connection and has two cables out. One goes to the WAN port of the Reyee gateway, and all IPv4 traffic in the house goes through it. The other goes straight into a spare port on the J4125, for IPv6 only; I’ll explain why below.
- Main router and wireless controller (AC): a Reyee RG-EG209MG-P. It hands out addresses and the gateway to every device, and its PoE ports power and connect each AP over a single cable while it manages them.
- APs: one RG-EAP272(MG) ceiling AP in the living room, one each in the two bedrooms, all Wi-Fi 7. The Mijia hub gateway in use sits near the living room TV. Walking around the flat, roaming has been smooth so far.
- Gaming room: two Ethernet ports, and the PC is wired.
- Rack: squeezed into the shoe cabinet in the foyer, holding the J4125, the Mac mini, the Reyee gateway and a spare Xiaomi hub gateway. The cabinet is small, so the PDU, short patch cables and cooling all had to be planned together.
Devices get their address from the Reyee gateway, which is also their gateway. IPv4 goes out through the AP, the Reyee and the modem; the J4125 is not on this path.
The TV uses the J4125 as both gateway and DNS. Between the TV and the J4125, the Reyee is just a switch. OpenClash splits the traffic: domestic sites go direct, YouTube and Netflix go through the proxy, and everything leaves via the Reyee again.
The modem hands out a single /64 and the Reyee cannot relay it. The J4125 has its own port straight into the modem and relays that prefix into the house; devices build their own public IPv6 addresses, and all IPv6 traffic passes through the J4125.
The point of a side router: when it reboots, updates or I break it, devices whose gateway is still the Reyee keep browsing and watching the media library. What stops is the proxy, IPv6, Home Assistant and remote access through it, and the TV pointed at the J4125 goes offline too.
J4125: one small box, several jobs
Why add another machine? Honestly, I had no choice. First, a public IPv4 address is very hard to get: the line sits behind carrier-grade NAT, nothing outside can connect in, and asking the ISP doesn’t guarantee one. Second, the ISP keeps the modem’s admin password, so I can’t switch it to bridge mode and let the router dial; and on the IPv6 side, the modem only hands out a prefix on its own LAN ports. The Reyee can’t help with either, so the work fell to the J4125.
The J4125 is a mini PC running router software, with a Celeron J4125 (four cores, about 10 W for the processor), four Intel 2.5G ports, running iStoreOS (based on OpenWrt). At home it is a side router: it has a fixed address in the home subnet, its own gateway points at the Reyee, and it doesn’t take over DHCP for the house. Devices still get their address and gateway from the Reyee by default; to use the J4125’s features, you point a device at it.
It has plenty to do:
- Proxy: OpenClash in fake-ip mode, domestic sites direct, everything else split by rules. A device that sets its gateway and DNS to the J4125 gets a transparent proxy; where changing the gateway is awkward, an app can use its HTTP/SOCKS port instead. The J4125’s own traffic also goes through the proxy, so Tailscale needs an exception, which comes up later.
- IPv6 relay: brings the modem’s IPv6 prefix into the house; more in the next section.
- Tailscale subnet router: advertises the home subnet, so devices without a client are reachable from outside.
- Home Assistant: in Docker, with the Mijia and Haier integrations and every automation.
- Camera backup: an external 1 TB surveillance drive, shared over SMB so the Mijia camera can back up to it on a schedule.
The heaviest user is actually the TV. Set it to a manual IP in its network settings, put the J4125 as gateway and DNS, and the YouTube and Netflix apps just work, with nothing extra installed on the TV. A few details matter:
- Change the DNS too. Fake-ip mode relies on the J4125’s DNS swapping each domain for an internal fake address; that is how the gateway knows to hand the traffic to the proxy. Change only the gateway and my setup can’t tell which traffic should go to the proxy.
- Don’t let the TV slip around on IPv6. The house has IPv6 now, but my OpenClash setup only proxies IPv4. Luckily I also set its DNS to return empty answers to IPv6 (AAAA) queries, so as long as the TV resolves through it, it only gets IPv4 addresses. That’s the other reason the DNS has to change.
- Block QUIC. YouTube defaults to QUIC over UDP. I blocked it in OpenClash, so the app falls back to TCP, which is steadier through the proxy.
- Netflix depends on the node. It detects proxies and data-centre IPs; whether it plays, and which region’s catalogue you see, depends on the node’s exit IP, not on how the house is set up.
The big win of a side router is a small blast radius. When the J4125 reboots, updates or gets broken by me, devices whose gateway is still the Reyee keep browsing and watching the media library; what stops is the proxy, IPv6, Home Assistant and any remote access that goes through the J4125 (the Mac mini runs Tailscale itself, so the media library is still reachable from outside). The TV pointed at the J4125 does have to wait for it, of course. The flip side is that a lot rests on one small box, so I back up its configuration before every change. When I change the network remotely, I also leave a script running that rolls back automatically after a timeout, so a mistake doesn’t mean a trip home.
IPv6: going around the main router
After the ISP enabled IPv6, the modem gets a prefix when it dials, but only advertises a single /64 on its LAN ports. Without the admin account I can’t turn on prefix delegation (DHCPv6-PD) on the modem, and trying to change the prefix length just throws an error.
So here’s a question: can that /64 be split again for the network behind the Reyee? No. A /64 is already the smallest subnet SLAAC can use; split it further and devices can’t configure their own addresses. That leaves two options: NAT66, or relaying the /64 into the house as is. The Reyee can do neither, no relay and no working NAT66, so the J4125 has to go around it:
- A cable of its own. One J4125 port stays out of the home bridge and plugs straight into a spare LAN port on the modem. IPv4 still goes through the Reyee; this cable only carries IPv6.
- Relay all three. OpenWrt’s built-in odhcpd sets RA, DHCPv6 and NDP to relay, with the modem side upstream and the home side downstream. Phones and laptops build their own public IPv6 addresses with SLAAC, no NAT needed.
- No address on the upstream port. The port facing the modem needs
accept_ra=2and must not ask the modem for a DHCPv6 address (reqaddress none); otherwise the modem answersNo Bindingevery forty seconds and the connection keeps dropping. Putaccept_rain the sysctl config, or the relay breaks after a reboot.
The potholes along the way, for the record:
- Enabled isn’t working. The ISP said it was switched on in their system, and the technician’s test on site still failed; three minutes after a power cycle of the modem, everything worked. So when “it’s enabled” doesn’t work, redial before drawing conclusions.
- New devices take a while. The relay has to learn a device’s address before outbound traffic flows. I saw one laptop get an address and wait about ten minutes before it could get online. Don’t start changing settings during that time.
- The prefix changes. In the ten-odd minutes after the modem redialed, the prefix changed several times before settling, and it will change again on the next redial. Don’t leave fixed DNS records pointing at addresses from that prefix.
- The main router’s own IPv6 is off. With IPv6 on the Reyee’s WAN port enabled, the J4125’s own IPv6 return path misbehaved; turning it off fixed it. I never fully worked out why, so it simply stays off.
- Look at the firewall separately. On IPv4, carrier NAT stops anything outside from connecting in; IPv6 has no such layer. Right after I got it working, a service port on the J4125 that was meant for the LAN only turned out to be reachable from the internet, and I quickly removed the inbound rule. The modem side now denies inbound traffic by default.
After all that, the biggest payoff was actually in Tailscale. On IPv4, UPnP and NAT-PMP are off on the Reyee, and the ports carrier NAT maps outward change every few minutes, so hole punching often failed and traffic went through a relay. With IPv6, my phone on mobile data can connect directly to home, and the old flat’s router reaches the J4125 directly in about 6 ms.
The router side had its own accident: the AI changed the DNS through a reverse-engineered API, and that night the whole network had problems. Since then, “the router is read-only” has been an iron rule; more on that later.
Devices: check for Mijia first
I usually look at Mijia first when choosing devices, for a simple reason: fewer platforms to maintain. Mijia covers the everyday switches, lights and sensors, and Xiaomi’s official xiaomi_home integration brings them into Home Assistant. Note that a device working in Mijia doesn’t mean all its features reach Home Assistant; check the exact model and the entities you need before buying. The official notes list what’s supported.
Most of my devices also use Mijia’s Bluetooth Mesh 2.0: the switches, thermostats and valve controller. They don’t use Wi-Fi; they talk to the hub gateway directly, and mains-powered ones relay each other’s signals. Together with automations that run on the gateway, when the broadband goes down the switches still turn on lights, scenes still run and a leak still closes the valve. What stops is anything that needs the cloud, like controlling things from my phone when I’m out or having Xiao Ai make announcements. It also makes the hub gateway the most important device in the house, which is why I keep a spare.
Here is what I chose and what I paid at the time, some of it bought second-hand on Xianyu (China’s second-hand marketplace). Prices are in yuan, for reference only, not current quotes.
| Purpose | My choice | Price then |
|---|---|---|
| Mijia gateway | Xiaomi hub gateway ×2 (one in use, one spare) | ~¥300 each |
| Wall control | Xiaomi smart control panel ×2, with built-in presence sensing | ~¥300–370 each |
| Switches | Linptech Bluetooth Mesh smart switches, a batch bought on Xianyu | Used: 1-gang ¥48, 2-gang ¥58, 3-gang ¥68 |
| AC integration | Zhonghong B19SE central AC controller | ¥558 |
| Thermostats | YOOMI 3-in-1 thermostat MESH2.0 ×5, replacing the old panels | ~¥224 each |
| Leak shut-off | Diamond valve controller Mesh2.0 + Xiaomi Flood Detector 2 ×6 | Valve ¥105–153, sensors ~¥60 each |
| Presence | Linptech presence downlights and spotlights, Linptech ES3 presence sensor | Depends on model |
| Announcements | Xiaomi Smart Speaker Pro | Depends on seller |
| Air purifier | Mijia Air Purifier 6 Pro, with PM2.5 and formaldehyde sensing | Depends on seller |
| Outdoor camera | Xiaomi Outdoor Camera 4 Pro + 64 GB microSD card | ¥404 + ¥98 |
| Computers | J4125 mini PC, Mac mini | Depends on spec |
How do you get an old central AC into Mijia? The original wired panels can’t connect, so I did it in two steps. First, a Zhonghong B19SE controller on the AC’s communication bus makes all 6 indoor units show up in Mijia. Then YOOMI 3-in-1 thermostats replace the old wall panels, each one handling the AC, underfloor heating and fresh air. Check that your AC brand, model and protocol are supported before trying this. One more thing: the panel’s “AC switch” directly controls the indoor unit, so don’t treat it as a harmless virtual button when testing (you’ll see later that this is exactly where I tripped).
Smart lights need constant power. Linptech switch buttons can be set to “wireless mode”: the button only sends a signal and no longer cuts the light’s power, and a Mijia automation decides what a press does. For example, the 3-gang switch by our bedroom door has three buttons: “Sleep”, “Cosy” and “Bright”. The lights stay online after they’re turned off, so the next scene can still find them. The cost is that the buttons now depend on the gateway and its rules working too.
The leak shut-off is the best money I spent. Six flood sensors sit in the kitchen, under the dishwasher, under the washer, in both bathrooms and on the balcony. If any of them goes off, the Mijia gateway closes the main water valve locally. Sensors plus valve came to under ¥600. Once it’s installed, actually test it, and check that an alarm really closes the valve.
The Haier washer, dryer and fridge come in through the Haier integration from HACS, and weather comes from QWeather. All of this cross-platform data is Home Assistant’s job.
How Mijia and Home Assistant split the work
My Mijia setup has 42 automations, and every one of them runs locally on the gateway: the lights and scenes behind each switch button, presence-triggered lights after dark, and the leak shut-off. In the foyer, for example, the light only comes on when someone is there and the ambient light is under 100 lux. None of this needs the internet or Home Assistant, only power and working local communication.
Home Assistant adds three things:
- Cross-brand automations. When the Haier washer finishes, the Xiao Ai speaker says so.
- Combining conditions. Deciding “nobody was home a moment ago, and now someone is back” means looking at sensors, lights and time together.
- One place for everything. The dashboard calls Mijia’s manual scenes directly, like Away, Movie and Dinner, while the scenes themselves are still maintained in Mijia. Our “Away” scene touches 63 devices at once, including the lights, the fresh-air system, all 6 air conditioners and the outdoor camera; there’s no point writing it twice.
After adding or removing devices in Mijia, remember to sync them in xiaomi_home under “Configure → Update device list”, and the dashboard’s entities may need adjusting too. I now hand this kind of gap-hunting to the AI and have it compare the device list against the entity registry.
Automations still have to survive real life
These are the rules actually running in Home Assistant. The temperature thresholds and announcement hours fit my devices and our routine; another home would need its own.
| Type | Automation | Trigger and limits |
|---|---|---|
| Safety | Leak alarm | Any time; overrides do-not-disturb, unmutes, volume at least 60%, repeats every minute, stops when the sensor clears |
| Safety | Fridge door open | Any door open for more than 2 minutes |
| Safety | Fridge or freezer too warm | Fridge above 8 °C or freezer above −12 °C for 20 minutes; if found at night, checked again at 7:30 |
| Chores | Washer done, dryer done | Only 07:30–22:30, at most once per 30 minutes |
| Chores | Laundry left in the drum | An hour after it’s done with no sign of anyone taking it out; the door sensor is unreliable, so someone on the laundry balcony is used instead |
| Chores | Detergent, softener, filter reminders | Delayed 20 seconds so they don’t collide with “done”; at most once per 12 hours |
| Chores | Weekly battery check | Saturdays at 10 am; silent if everything is fine |
| Greeting | Welcome home | 06:30–23:30, only when all six conditions hold, at most once per two hours |
| Greeting | Weekday good morning | Skips public holidays; plays when someone was home last night and first leaves the bedroom in the morning, with a weather note |
| Logging | Robot vacuum started, laundry waiting | Silent; only records the time, for the greetings to mention |
| Patrol | Leaks and fridge doors | Checked every 5 minutes and after every restart, to catch alarms that might have been missed |
Announcements have to queue, too. The Xiao Ai speaker switches to new text immediately, so if you send two sentences in a row, the second cuts off the first. All messages therefore go through one script: normal ones queue, and urgent ones like a leak can jump ahead and interrupt. The script estimates how long a sentence takes at 0.3 seconds per character plus 3 seconds, and sends the next one when that time is up. It’s only an estimate, of course; the script has no idea whether the speaker has actually finished.
The washer’s status can’t always be trusted
The “laundry left in the drum” rule originally relied on the door sensor. The first time we actually did a wash, the reminder never came. The history showed that the end-of-cycle state never triggered as expected, and the door was worse: it reported “closed” the whole time.
With no completion signal, of course the reminder never fired; and a door that always reads “closed” can’t tell whether anyone took the clothes out. I then had the AI change two things: use the wash phase to tell whether the cycle is done, and use the presence sensor on the laundry balcony to guess whether someone went to take the clothes out. Visiting the balcony doesn’t mean the laundry was collected, so this will miss sometimes, but it beats watching a door status that never changes.
Walking in isn’t always coming home
The study, gaming room, kitchen and dining area have no presence sensors, and of the two balconies only the laundry balcony has one. So “nobody is home” isn’t a single value I can read. It has to be pieced together from the signals I have.
- Time between 06:30 and 23:3018:40
- No greeting in the last two hoursYes
- Foyer empty for the previous 30 minutesGone 10 h
- No other presence sensor sees anyoneNobody, and no change for 30 minutes
- All room lights offAll off
- Speaker not playingYes
Good evening, welcome home! The washer has finished, don't forget the clothes. Long day, sit down for a bit.
I count the lights too: the study has no sensor, but if its light is on, I assume someone is in there. And if someone is in the study with the light off? Then it can’t see them. Try “Study, light off” in the figure: it misses the person and says welcome anyway. A presence sensor would fix it; I just haven’t bought one yet.
Restarts cause trouble as well. When Home Assistant restarts, the state timestamps of entities can all refresh together, and then they can’t tell how long we were out. So if the relevant timestamps all fall within three minutes of each other, it skips lines that depend on the duration, like “back so soon”.
The greetings are put together by a program the AI helped write: an opening picked by time of day, then one or two relevant things like finished laundry, the weather or the weekend. Both our birthdays, lunar festivals and solar terms are in there too, worked out in advance up to 2030, and the last 45 lines are remembered so it doesn’t say the same thing every day. To be clear, it does not ask a language model to write a new line each time.
Media library: send a link, get it in the library
The media library is Emby on the Mac mini, spread over two drives. As I write this it has 273 films and 28 series, 2251 episodes in total. The living room TV runs the Emby client, and phones and laptops use Tailscale when away.
The AI also helped me wire up the import pipeline. Now I just give it a cloud-drive share link and say “import”, and it takes it from there:
- Read what’s shared and, with Sonarr (series) or Radarr (films), confirm which title and which episodes it is;
- Save it to my own cloud drive, which alist exposes over WebDAV;
- Download incrementally with rclone, usually 30–36 MB/s with a paid membership;
- Hand it to Sonarr or Radarr to rename and file on the right drive;
- Tell Emby to refresh and fetch posters and descriptions.
Two problems came up during imports. First, Sonarr’s DownloadedEpisodesScan couldn’t claim files from this external download flow, so I switched to ManualImport. Second, anime on cloud drives is often just 01.mkv and so on, so you have to confirm the season and episode before mapping it to S01E01; guessing from file order goes wrong.
I gave the import pipeline one hard rule: files that already exist are always skipped, never deleted or replaced. Upgrading quality is a separate request, with the scope confirmed before anything is touched, so a library clean-up never quietly deletes an old version.
The TV had its own small episode. The Emby client on our TCL TV (2.5.03g) could log in the first time, but after leaving and coming back it just spun, saying “invalid username or password”. I had the AI capture packets on the J4125, and it found the app wasn’t sending a login request at all. Following the auto-login thread, I changed the start-up option to “log in as current user”, and that fixed it.
Tailscale: getting home from outside
As I said, our broadband has no public IPv4 and sits behind carrier-grade NAT, so DDNS plus port forwarding is out. Instead, Tailscale puts the phones, laptops and home machines on one private network, and Home Assistant, Emby and the router’s admin page never face the internet directly.
http://<Mac tailnet address>:8096The phone and the Mac mini are both on the tailnet, so the Emby app just uses the Mac's Tailscale address. If hole punching works, they connect peer to peer; if not, traffic goes through my own DERP relay in Chengdu. The relay is about 10 ms away from me, so for video the real limit is bandwidth.
http://<J4125 tailnet address>:8123The Home Assistant app uses the J4125's Tailscale address. There's no need to expose HA's port to the internet, and access follows the tailnet policy.
https://<J4125>.<tailnet>.ts.net:8443The Reyee gateway can't run Tailscale, so requests reach the J4125 first and are forwarded over the LAN. Tailscale Serve on the J4125 adds HTTPS with an automatically issued certificate, and only devices on the tailnet can open it.
http://<J4125 tailnet address>:8123/api/statesMuse runs in a Meta cloud VM; with Tailscale installed it is just another device on the tailnet. For now it reaches home through the relay and reads states with an HA long-lived token.
Each side advertises its own subnetThe Redmi AX6000 at the old flat runs Tailscale too. Each side advertises a non-overlapping subnet; they can reach each other only after forwarding, route acceptance and access rules are set up. I measured about 6 ms over a direct IPv6 connection.
This is how mine is set up:
- Put the machines on the tailnet. The phones, laptops, Mac mini and J4125 all run Tailscale, and so does the Redmi AX6000 at the old flat.
- Leave a way in for devices that can’t run a client. With IP forwarding on, the J4125 advertises the home subnet with
tailscale set --advertise-routes=192.168.110.0/24; approve the route in the admin console and set up the access rules. Clients also have to accept the route, which is worth checking on Linux in particular. The subnet router docs have the details. - Use a nearby relay when direct fails. Latency to the official DERP servers is fairly high from here, so I run my own on a cloud server in Chengdu, about 10 ms away. That number depends on where you are and your line, and for remote video bandwidth matters more.
- Put HTTPS on the admin page. The router’s own admin page uses a self-signed certificate, so I reach it at
https://<machine>.<tailnet>.ts.net:8443instead. On the J4125,tailscale serve --bg --https=8443 https+insecure://<router LAN address>:443makes Serve issue a proper certificate and reverse-proxy to it. The official examples mostly proxy a local port, but a LAN address worked in my testing. Access is still limited by the tailnet policy.
Also, I change individual Tailscale settings with tailscale set. If tailscale up is missing existing flags, it warns you and prints the full command with the old settings; what actually resets unspecified settings is --reset, as the command reference explains.
If you also run Clash, look at the DNS and proxy rules together:
- OpenClash (on the router): make sure Tailscale’s connections aren’t diverted by the transparent proxy, and that its domains still resolve. I once had a proxy node die; Tailscale’s existing connections kept working, but the control plane was cut off for over four days before I noticed.
- Clash Verge (on the Mac): fake-ip mode used to resolve
*.ts.netto fake 198.18.x.x addresses. The fix is to add+.ts.nettofake-ip-filterand send those domains to Tailscale’s DNS at100.100.100.100, which only works while Tailscale on that machine is connected.
An AI that can check on the house
When I’m out and just want to ask how things are at home, I use Muse. Meta released this assistant in September 2026; it runs in its own cloud VM and keeps working after you close the app.
I installed Tailscale in Muse’s VM so it can reach Home Assistant, and created a separate long-lived access token called muse, so it’s easy to find and revoke later. Once it knows the usual entities, I can just ask “Is the fridge door closed?” or “Has the washer finished?”, and it calls the REST API, reads the state and answers in a sentence. On work trips, I can also have it check periodically and report back.
There’s a catch, though: naming a token “read-only” doesn’t make it read-only. A long-lived token carries its account’s permissions, a regular user can control entities, and calling the token muse is just for bookkeeping. Home Assistant’s permission docs distinguish regular users from read-only permissions.
What about limiting it with Tailscale access rules? I can narrow what it reaches to port 8123 on the J4125, but reading states and controlling devices use the same port. Telling it in standing instructions not to touch the AC, the heating or the locks only constrains its behaviour; it doesn’t replace real API permissions. Truly read-only access needs a verified read-only grant, or a layer in between that only exposes chosen states, with direct access to Home Assistant blocked. That’s the next thing I want to tighten; the current direct-token setup can’t do it.
And don’t forget: whatever it reads ends up in Muse’s cloud environment. Tailscale only protects the connection; it doesn’t keep the state of the house local.
House rules for letting AI act
The AI has saved me a lot of configuration time, but it has also slipped up. Once, while testing a thermostat, it ran the living room central AC in cooling mode for 37 seconds (yes, that “AC switch” from earlier). Another time it changed the router’s DNS through a reverse-engineered API and only replicated half of what the web page’s “Save” button does; that night the whole network threw alerts, and it took resetting the APs one by one the next day to recover. I still haven’t fully pinned down the cause of the second one, but I really don’t want it to happen again.
So now it works under a few rules, sorted by what a change can affect and whether a mistake can be undone:
- Back up before changing configuration. After a Home Assistant change, run
check_configand confirm both the exit status and the output are clean before reloading; passing the check still isn’t the end, it has to actually behave as expected. - Ask me before touching real devices. Even sending “off” to underfloor heating that’s already off, it has to watch whether anything else gets triggered along with it.
- No testing at night. From 21:45 to 7:30 the next day: no lights, no sound, no scenes that change state. These hours are for debugging and are stricter than the everyday reminders.
- I type the passwords. The AI can open the page; logging in is left to me.
- The router is read-only. The existing scripts only keep the read endpoints, and it isn’t allowed to write new scripts to get around that. Still, as long as it holds admin credentials, this is an agreement, not technical isolation.
- Write down causes and fixes. A new session may not remember the last one, so reading the notes before starting saves a lot of wrong turns.
How it feels after living with it
Tailscale is genuinely great. No public IP, no fiddling with port forwarding or DDNS; install the client and you can reach home from outside. Since IPv6 started working, my phone connects directly most of the time when I’m out, and the occasional relay is good enough. Watching films away from home, changing settings, letting Muse check the house: it all runs on Tailscale.
The Reyee AC and APs are genuinely great too. Solidly built, Wi-Fi 7 really is fast, and the admin interface is modern and professional; how many devices each AP has and how good the signal is, all clear at a glance. There’s no need to pay the official price, though: resellers on Xianyu sell them brand new, for a lot less.
Living comfortably matters more than extra features
Some features I left out on purpose: no announcement when the camera spots people or cars, none when the robot vacuum finishes, and no daily weather briefing. Chore reminders mostly stay between 07:30 and 22:30, the welcome greeting has its own hours and conditions, and only a leak raises the alarm at any time. I’ll keep adjusting the schedule; if the reminders get annoying, the whole thing has missed the point.
Finally, about AI. Things I didn’t understand, found too complicated or didn’t dare touch before, like IPv6 relaying, automations with a long list of conditions, or capturing packets to find out why the TV couldn’t log in, I can now hand to it, and it does them more carefully and thoroughly than I would. Honestly, that’s the biggest takeaway from this whole project. There is a cost: I no longer know every line of the configuration the way I used to, so I keep backups before changes and try things myself afterwards. A washer reporting the wrong state is not something you can spot by reading the code.
What makes me happiest now is getting the occasional reminder that the laundry is done, and a house that stays quiet when someone is asleep at night.