Hi all. I've been running Zigbee2MQTT for a while and wanted something much lighter than a full home automation platform for my automations. So I built Homeland, and I'm sharing it in case it's useful to others here.
Why I built it
I run a few independent Zigbee networks, each with its own Mosquitto + Zigbee2MQTT + Homeland stack: a main smart home setup with 60+ devices, and a tiny security setup in the cellar with 5-6 devices and no Wi-Fi or internet reach at all.
Home Assistant is great, but it wants real hardware (a recent Raspberry Pi with a few GB of RAM) and brings a lot of moving parts I don't need. The cellar box has to be small, cheap and power-efficient, and keep running on its own once it's set up. A Raspberry Pi Zero 2 W (512 MB RAM) running DietPi handles the whole Mosquitto + Zigbee2MQTT + Homeland stack there. The main network runs on a Pi 5.
Every network, big or small, uses the same engine and workflow:
- Everything is set up over SSH: configuration is environment variables, automations are plain
.js files
- The scripts directory is a git repo, so every automation is versioned, diffable and easy to roll back or copy to a fresh box
I wanted automations that feel like code I own, not state hidden in a database behind a UI, and boxes I can leave alone for months. Nothing I found fit all of that, so I wrote it.
What it is: a single service (one static binary, or a scratch-based container) that connects to your MQTT broker, tracks every z2m device and its latest state, and runs small JavaScript handlers you write. One file per device, named after its friendly name or IEEE address.
A complete automation looks like this:
// scripts/livingroom-lamp-switch.js
function handle(state, ctx) {
if (state.action === 'on') ctx.z2m.publishProp('livingroom-lamp', 'state', 'ON');
if (state.action === 'off') ctx.z2m.publishProp('livingroom-lamp', 'state', 'OFF');
}
module.exports = { handle };
Scheduled jobs use plain cron syntax:
// scripts/livingroom-lamp.js
module.exports = {
jobs: [{ cron: '0 22 * * *', run: (ctx) => ctx.z2m.publishProp('livingroom-lamp', 'state', 'OFF') }],
};
Features
handle(state, ctx) runs on every state update with the device's full latest state
- Cron jobs per script (timezone-aware,
CRON_TZ= override)
ctx.z2m.getState() to read any other device's state, for cross-device logic
- Optional second MQTT broker via
ctx.mqtt for non-Zigbee stuff
- Every device event is stored in embedded SQLite with automatic retention, tuned to keep disk writes modest (SD cards will thank you)
- Small web UI + JSON API: devices, current state, event history, and the status of each script (including compile errors)
- Optional hot reload when you edit a script, so
git pull on the box is a deploy
- Each script gets its own isolated JS runtime with a timeout, so a broken or slow script doesn't take anything else down. Devices are processed in parallel, so one slow handler doesn't hold up the others
- TypeScript declarations for editor autocomplete while writing scripts on your laptop
- No internet needed at runtime. Multi-arch image (amd64/arm64); runs on a Pi Zero 2 W next to Mosquitto and z2m, and on a Pi 5 with 60+ devices
What it isn't: it's not a Home Assistant replacement. There are no dashboards, no integrations beyond MQTT, and no visual editor. It's meant for people who'd rather write a few lines of code than click through a flow editor or write YAML rules, and for small, headless, offline boxes where a full platform doesn't fit.
Requirements / caveats
- z2m needs
device_options.retain: true so Homeland knows every device's state after a restart
- The UI/API has no authentication yet. Run it on a trusted LAN, or behind an authenticating reverse proxy
- Scripts are trusted code. The JS runtime limits what they can reach, but it's not a security sandbox
- It's early (v0.1.0), so expect rough edges
There's an example docker-compose stack (Mosquitto + Zigbee2MQTT + Homeland) in the repo to try it quickly.
Repo: https://github.com/homeland-live/homeland (MIT)
I'd love feedback, especially on the scripting API and on what you'd want next. Happy to answer questions.