SmartOneg / docs

SmartOneg documentation

Everything to install, configure, and integrate SmartOneg.

Installation (Docker)

Docker is the recommended install on every platform. On macOS I recommend OrbStack (a lighter, faster drop-in for Docker Desktop); on Windows use Docker Desktop; on Linux, Docker Engine. Then:

mkdir smart-oneg && cd smart-oneg
wget -O docker-compose.yml https://github.com/moshechaikin/smart-oneg/releases/latest/download/docker-compose.yml
docker compose up -d

Open http://<host>:1836 and follow the setup wizard. The image is multi-arch (amd64 + arm64), so it runs on a PC, a Mac, or a Raspberry Pi 3+/4/5. Prefer no Docker? Use macOS (Homebrew) or Linux (pm2).

Fixing data-folder permissions

The container runs as a non-root user (uid 1000), so the host ./data folder must be writable by that user. If the app logs something like “no config found” that never persists, or you see permission errors, fix ownership on the host (run these from the folder that holds your compose file, or point them at wherever your ./data lives):

sudo chown -R 1000:1000 ./data
sudo chmod -R u+rwX,g+rwX ./data

The same two commands apply anywhere (just point them at your ./data folder). u+rwX,g+rwX grants read/write, and the capital X adds “enter” permission only to directories, not files.

Opening firewall ports

SmartOneg has to reach your bridges/devices on your LAN (Lutron on port 23, EnvisaLink on 4025, Home Assistant/Hubitat on their web ports, etc.). If you run a host firewall, a blocked port means the container can connect to nothing and every bridge shows offline. Make sure:

macOS (Homebrew)

Prefer not to run Docker on your Mac? Install SmartOneg natively with Homebrew. It runs as a background service (launchd) that starts at login:

brew install moshechaikin/tap/smart-oneg
brew services start smart-oneg

Open http://localhost:1836 and follow the setup wizard.

Update with brew upgrade smart-oneg. Your data (config, schedules, backups) lives in $(brew --prefix)/var/smart-oneg and is preserved across upgrades.

Keep the Mac awake (so it stays a server)

Homebrew keeps the app alive, but macOS will still sleep and stop serving. For an always-on Mac:

Linux without Docker (pm2)

On Linux (Raspberry Pi OS, Debian, Ubuntu…) Docker is still recommended (see Installation (Docker) above), but it isn't required. SmartOneg is a plain Node.js app with no build step (the UI ships prebuilt) and pure-JavaScript runtime dependencies (nothing to compile), so you can run it straight from source and keep it alive with pm2, a tiny process manager that restarts the app on crash and at boot.

Raspberry Pi. A Pi Zero 2 W (arm64, ~$15, once RAM prices come back from Mars and land back on Earth) or a Pi 3/4/5 is the easy target, install the normal Node 22 below and follow the steps; the Zero 2 W runs SmartOneg comfortably (idle ≈ 110 MB RAM).

1. Install Node + pm2

Install Node 22 (via your distro, nvm, or NodeSource), then pm2:

node -v            # must be v22 or newer
npm install -g pm2

2. Get the code and start it

git clone https://github.com/moshechaikin/smart-oneg.git
cd smart-oneg
npm ci --omit=dev                       # runtime deps only
pm2 start server/index.js --name smart-oneg
pm2 save                                # remember this process list
pm2 startup                             # prints one sudo command, run it to start on boot

Open http://<host>:1836 and follow the setup wizard. Your data lives in ./data inside the clone (git never touches it, it's ignored), exactly like the Docker ./data volume. Set a different port with PORT if needed (pm2 start server/index.js --name smart-oneg --update-env after export PORT=8080).

pm2 handles the "auto-restart if it crashes" part; pm2 logs smart-oneg tails the output, pm2 restart smart-oneg restarts it.

Keep it powered, a UPS is recommended. SmartOneg only automates while it's running, so plug whatever it runs on into a small UPS (uninterruptible power supply, a battery backup). The controller draws very little, so even an inexpensive UPS rides out a power flicker or a short outage, and your Shabbos/Yom Tov schedule keeps firing instead of going dark mid-Oneg. Pair it with your host's auto power-on after an outage, macOS: Start up automatically after a power failure; a PC / mini-PC: the BIOS Restore on AC Power Loss setting; a Raspberry Pi powers on by itself when power returns. On boot, SmartOneg automatically re-applies the state your schedule says should be active, so it recovers even from an outage that outlasts the battery.

Privacy & local-first

SmartOneg runs entirely on your own network, with no account, no cloud backend, and no telemetry or analytics of any kind. Read the full Privacy Policy for details.

Updating & backups

Because all data is in the ./data volume, updating the image never touches your config. SmartOneg checks smartoneg.com/version.json about once a day and shows a dot on the About button plus a card in Settings → System → Software updates when a newer release exists, with the release notes and version numbers. If you'd rather the app never reach out on its own, toggle “Automatically check for updates” off there, that stops all automatic outbound requests; the manual Check now button still works.

One-click update from the app

The default compose file mounts the Docker socket, so the Update button in Settings can pull the new image and restart the app for you (it launches a one-shot Watchtower against just the SmartOneg container; your ./data is untouched). The app runs as a non-root user, so it needs the host's docker group to use the socket, if the button reports it “could not start,” find the gid with getent group docker (often 999 on Linux) and add it under group_add in the compose file. Docker Desktop on Mac/Windows usually needs nothing.

Security: the Docker socket is root-equivalent access to the host. Anything that can reach it can take over the machine. That's an acceptable trade on a private home LAN, which is what SmartOneg is designed for. But if you expose the app to the internet (a port-forward or public tunnel), you should either remove the /var/run/docker.sock line from the compose file, updates then fall back to a copy-paste command, and nothing else is lost, and/or put a real access layer in front of the app: Cloudflare Access, a Tailscale/WireGuard VPN, or an authenticating reverse proxy, rather than relying only on SmartOneg's own login, which is also why SmartOneg doesn't offer 2FA itself, a proper access layer covers that need instead.

Updating manually

You can always update from the host instead (this is the fallback when the socket isn't mounted):

docker compose pull && docker compose up -d

For fully hands-off updates, uncomment the Watchtower service in the compose file, it pulls new releases automatically on a schedule. Releases are never pushed during Shabbos or Yom Tov, so this won't restart the app mid-Oneg, but since it updates unattended, it's worth checking in on it after you set it up to make sure everything still comes up cleanly (no breaking changes are expected any time soon, but it's still worth a look).

SmartOneg also writes a nightly on-disk backup of your entire config (the last 14 days are kept), and Settings → Backup & Restore lets you export, import, download any snapshot, or restore one with a click.

Backup instance (failover)

Run a second copy of SmartOneg on another device (a spare Raspberry Pi is ideal) and choose Standby in its setup wizard, pointing it at your main instance. It keeps an up-to-date copy of all your settings and quietly watches the main instance. If the main one ever goes down, the standby takes over your lights on its own, then hands control back once the main instance is healthy again.

Remote access & notifications

To reach the app away from home and enable web push (especially iOS PWA push, which requires HTTPS), expose it over Tailscale (or Tailscale Funnel), Tailscale Serve for HTTPS within your tailnet only, or Cloudflare Tunnel, both terminate HTTPS. Then install it as a PWA (Add to Home Screen) and enable push from Settings → Notifications. Email and ntfy work regardless of HTTPS.

If you expose it to the public internet (a Funnel or Tunnel anyone can reach): the app's login has rate limiting, timing-safe checks and hardened sessions, but an identity-aware layer in front is still stronger, Cloudflare Access or plain Tailscale (private tailnet, no Funnel) means strangers never reach the login page at all. Note that behind a shared tunnel, heavy failed-login spam from strangers can temporarily rate-limit remote logins for everyone (LAN logins are unaffected, each LAN device gets its own limit bucket).

Password reset (locked out)

Forgot the password (or someone changed it)? Reset it from the machine the app runs on:

docker compose exec smart-oneg npm run reset-password -- [email protected] 'new-password'
docker compose restart smart-oneg

Append --clear-sessions to the end of the reset-password command above (i.e. npm run reset-password -- [email protected] 'new-password' --clear-sessions, still followed by the docker compose restart line) to also log out every signed-in device (do this if you suspect the old password was compromised). Running without Docker: DATA_DIR=./data npm run reset-password -- [email protected] 'new-password' from the app folder, then restart the app. If you run a backup instance, the new credentials mirror to it automatically within seconds.

Devices & bridges

Add devices from the Devices page (“Add devices”). Supported sources:

SourceHowChild Lock
Lutron CasétaImport the integration reportPush (instant)
Home AssistantLong-lived token; REST + websocketPush (instant)
Homebridgeconfig-ui-x API (insecure mode -I)Polled (a few seconds)
HubitatMaker API app id + tokenWebhook or polled
MatterPairing code (experimental)Push (subscriptions)
EcobeePIN authorization (cloud)n/a (thermostat)
EnvisaLink alarmLocal TPI (port 4025); DSC paneln/a (arm/bypass)

Home Assistant automations, scripts & scenes: beyond ordinary entities (lights, switches, thermostats…), any automation.*, script.*, or scene.* in HA imports too, as a momentary “Run” device (they land in their own Automations area on the Devices page by default). Setting one on in a rule or scene fires it (triggers the automation / runs the script / activates the scene); off is a deliberate no-op, since turning an automation off in HA would disable it, not just stop running it. This is also the way to bring in any other alarm/security system you've already got arming, disarming, or bypass wired up as an HA automation, see Alarm systems.

Device types: each device can be a light, dimmer, smart plug/outlet, fan, shade/blind, thermostat (with °C/°F display and preset/heat/cool modes), a smart lock, a robot vacuum, a fridge whose Sabbath mode is exposed as a switch, or an alarm partition / bypass zone (see below). When you import, assign each device to one of your existing rooms so everything stays organized, and double-check its type is set correctly, a dimmer imported as a plain light (or vice versa) won't offer the right controls (brightness levels, etc.).

A note on smart locks: a lock is meant to be used the same way lights are, as a scheduled action you set up before Shabbos or Yom Tov, for example auto-locking the front door at a set time Friday night. Operating a lock by hand during the day itself (a keypad, an app, or the manual Lock/Unlock button here) directly runs an electronic device, so the value is the timer-style automation, not tapping Lock or Unlock on Shabbos. A lock is also a security device, so lean toward scheduled auto-locking rather than auto-unlocking while you are asleep or away.

Lutron Caséta

Lutron's Caséta lineup, switches, dimmers, plug-in modules, and Pico remotes, connects through the Smart Bridge PRO (L-BDGPRO2 model only - you may only find these from specialized online retailers or eBay). Browse the full range on the Caséta website.

  1. Pair your switches, dimmers, plug-in modules, and Pico remotes to the Smart Bridge PRO using the Lutron app, as usual.
  2. Once all your devices are paired, open the Settings gear → Advanced → Integration and turn Telnet Support ON.
  3. From that same Advanced menu, give the bridge a static IP, either set it directly there under network settings, or reserve one for it in your router's DHCP settings.
  4. Tap Send Integration Report to email yourself the JSON listing every device paired to the bridge.
  5. In SmartOneg, go to Settings → Bridges & hubs, enable Use a Lutron bridge, fill in the bridge's IP, and save.
  6. Then go to Devices → Add devices → Lutron Caséta and import that JSON report.

Alarm systems (EnvisaLink)

SmartOneg can arm/disarm a DSC alarm panel and bypass individual zones through an EyezOn EnvisaLink module (target: the EVL-4EZR; the EVL-3 and EVL-4 speak the same protocol). It talks to the board directly over your local network, no cloud, and no Home Assistant required.

Why it's built in: on Shabbos and Yom Tov you often want to bypass interior/motion zones so walking through the house doesn't trip a sensor, then restore them after havdalah. SmartOneg exposes the panel as ordinary on/off devices, so a schedule rule or scene can do exactly that.

Installing the module

Connecting SmartOneg

  1. Settings → Bridges & hubs → EnvisaLink alarm: enter the module's IP (port 4025 by default), the TPI password, your alarm code (needed to disarm), and the partition (usually 1). Pick how it arms, stay (interior-aware), away, or night.
  2. Under Alarm devices, add the partition (arm/disarm) and one bypass zone per DSC zone number you want to control. They show up on the Devices page.
  3. Use them in schedules and scenes like any device: on = armed / bypassed, off = disarmed / active. For example, one Shabbos rule to bypass the motion zones at candle lighting and another to restore them after havdalah.

Only DSC panels are supported directly today. Your alarm code is stored on-device alongside your other credentials, and disarming uses it automatically. If you have a different alarm/security system with arming, disarming, or bypass exposed as an entity or automation in Home Assistant, you can import it into SmartOneg the same way as any other Home Assistant device, see Devices & bridges.

Schedules & rules

A rule is what happens and when. The “what” is a device (or several) turned on/off or set to a brightness (the label adapts to the device, lock/unlock, arm/disarm, open/close, start/dock, and so on), a thermostat action (hold temperature, resume schedule, preset, or HVAC mode), a scene start/end, a flash reminder, or an automation run, enabled, or disabled. The “when” is either a fixed clock time or an offset relative to a zman (candle lighting, shkia, tzeis, alos, plag, sof zman shma/tefilla). Every rule resolves to a real time in the live preview before you save, including rare situations decades out.

Fixed time vs. relative to a zman

Prefer relative to a zman whenever the intent is tied to the day, because it tracks the season automatically. “Turn the dining room on 1 hour before shkia” is right in June and December without you touching it; “turn it on at 6:45 PM” is only right for a few weeks a year. Reach for a fixed time when the intent really is a wall-clock moment, “kids' room off at 7:30 PM,” “front porch off at 11:00 PM.” You can combine them: a fixed time with clamps, or a zman offset with a floor/ceiling (below).

“Copy from…” vs. “Start from the Regular schedule”

These sound similar but behave very differently:

Rule of thumb: if you want a living variation of Regular, inherit; if you want a fresh copy you'll evolve on its own, Copy from…

Clamps vs. seasonal conditions

Both live under a rule's fine-tuning, but they do different jobs, one decides whether a rule fires, the other decides when:

The reason both exist: a zman-relative time drifts across the year, and each tool tames that drift in a different way. Some real examples:

Rule of thumb: if every case you have is “fire or don't fire,” you only need conditions. Reach for the clamps when you want a floating zman-based time held inside a hard wall-clock window.

Situations & inheritance

Each day-type (Shabbos, Pesach I, …) has a Regular schedule plus situations for the years it lands differently, “Falls on Shabbos,” “Starts motzei Shabbos,” “Erev Pesach,” and so on. A situation can start from the Regular schedule as a base: Regular's rules appear as cards you can customize or disable per-situation, and future edits to Regular flow through automatically.

The workflow: Regular first, then inherit down

The intended way to set this up, once:

  1. Build your Regular schedules for everything, a Regular Shabbos, a Regular Pesach I, and so on. This is the 95% case and it's where you spend your time.
  2. For each special situation, check “Start from the Regular schedule,” then cross out the rules that don't apply and tweak the rest. A situation is almost always “Regular, but with a few changes,” so you're editing a handful of rules, not rewriting a day.

Because situations inherit live, a fix to your Regular schedule next year automatically improves every situation built on it.

Paired weekends (when a Yom Tov meets Shabbos)

A few times a decade, one weekend spans two kinds of day, e.g. Erev Pesach falls on Shabbos (2025, 2045), or Shavuos/Pesach starts right after Shabbos, or a Friday Yom Tov runs straight into Shabbos. Each half is a variation of its own natural parent:

So it's two small cross-out passes, one on each parent, not one merged mega-situation (there'd be nowhere coherent to put the Yom Tov day's rules, which belong to Pesach every year, not to one special Shabbos). The same shape applies to Shavuos-on-Shabbos and to a Friday Yom Tov leading into Shabbos.

Precedence & conflicts

There is no precedence, nothing “overrides” anything. Every schedule compiles onto one timeline and each rule fires at its moment. Two things keep that safe:

Guest mode layers on top

Guest mode is a temporary override switch on the dashboard (and set of rules under “Situations”) that layers over whatever's running: it changes only the exact devices and times you name, every other rule keeps going, and it auto-expires after that one Shabbos/Yom Tov. In any timeline preview, guest actions carry a blue Guest pill, those are intentional overrides, not conflicts.

Child Lock

Opt-in globally and per-device. During an active Shabbos/Yom Tov, SmartOneg watches the bridge for deviations; if a watched light ends up wrong, it's set back after a grace delay (how long it waits before restoring, 5–15s). Flipping the same switch N times in a row (2–10, letting it revert between flips) latches that device to its manual state until havdalah, the non-Jew escape hatch.

The flips only add up when they come in quick succession: the app keeps a rolling window of about grace + 25s between one flip and the next (so it always covers a “flip → wait for the restore → flip again” cycle, and scales with your grace delay). The moment there's a longer gap the counter resets, so a press one day and another the next, or a few scattered presses over an evening, never accumulate into an accidental hold. A genuine override sequence takes well under a minute. Presses are capped at 10 and grace at 15s precisely because a real helper won't stand there flipping a switch for minutes on end.

Two details worth knowing. First, the counter tracks flips that leave the device in the wrong state, not raw button presses. A press that returns it to where the schedule wants it isn't a deviation at all, so it never adds to the counter, it just cancels the pending restore. Because the counter only advances when the device is flipped away from its scheduled state, and that can only happen again once the device has gone back to that state in between (either the app restored it after the grace delay, or someone pressed it back), mashing the switch in one direction does nothing: pressing off five times while a light is already off counts as one, not five. The escape hatch is the deliberate cycle “flip it off, let it come back on, flip it off again,” repeated N times within the window. Second, counting and the grace delay are separate: each counted flip registers the instant it happens, while the grace delay governs only the restore and every flip restarts it, so during a legitimate back-and-forth the app doesn't fight you mid-stream, it waits until things settle and sets the device right once, grace seconds after your last flip.

By default Child Lock begins at candle lighting. Households that accept Shabbos early can set it to begin at the day's first rule instead. It only ever acts during Shabbos/Yom Tov, never on a weekday.

Scenes, guest, away & test mode

Scenes

A scene is a reusable, named group of device states, say a "mealtime" scene that puts the dining room chandelier at 60%, the hallway on, and the kitchen at 30%. You build it once on the Scenes page and then reference it from any schedule, so tweaking the scene updates every day that uses it, no need to touch each rule. The editor has a live preview: apply the scene to your real lights now to see it, and one click restores them to exactly where they were before the preview (it snapshots each device on entry and drives it back on exit).

Every scene has two halves: a start state and an optional end state. In a schedule you point a rule at a scene in one of two ways, Start scene or End scene. A Start scene rule drives every device in the scene to its start state at that rule's time. That is all most scenes ever need: "start the mealtime scene at candle lighting" sets the group and you are done. You never have to define an end state. Without one, the devices a scene set just stay where they are until another rule changes them.

The end state is what you turn on when you want a scene to also define how its group of devices should look later. Open the scene, switch on Customize what happens when the scene ends, and pick a target for each device: for lights Leave as is, Turn off, Turn on, or Dim to…; shades get Close / Open to…; thermostats get Resume program / Hold at…. Now a single End scene rule sets the entire group to that end state at once. So instead of writing a separate "off" rule for the chandelier, the hallway, and the kitchen at bedtime, you write one "end the mealtime scene" rule and the whole group lands on its defined end state together. A scene member you leave on Leave as is is untouched at end, which lets an end state turn some devices off while holding others.

When a scene defines an end state, its preview shows each device as a start → end progression, for example On → Off or 60% → 20%, so the change is obvious at a glance.

Guest mode (overrides)

Guest mode lets you flip specific devices for the next Shabbos or Yom Tov without rewriting your regular schedule. It layers on top: a guest rule replaces your normal rule for the same device only when the two happen at about the same time (within roughly 10 minutes of each other). So a guest "dining room chandelier on at candle lighting" wins over a scene that would otherwise turn that same chandelier off at candle lighting, and the two never fight over the light.

Rules that are further apart in time are left alone, because they are two separate changes, not an override. If your schedule turns a light on at 6:00 and another rule turns it off an hour later at 7:00, both still run in order (on, then off). Guest mode only settles the collision at the same moment, it does not touch the rest of your night.

In the day editor preview timeline, the rule a guest override replaces is shown struck through and marked overridden by guest, so you can see both what the scene would have done and what the guest changed. The simpler overview timeline (in a Shabbos or Yom Tov overview page) just shows the final result.

Away mode (presence simulation)

Going away for Shabbos or Yom Tov? Turn on away mode and pick a window, a single Shabbos, a whole festival (e.g. all of Sukkos) or just its first days / last days, or a custom date range. Instead of a separate schedule, it takes your own schedule and layers bounded randomness on it so the house looks occupied:

When it applies. Away mode runs on the exact same window as Child Lock: from candle lighting to havdalah of each Shabbos/Yom Tov in your chosen range. It never touches erev preparation before candle lighting, wind-down after havdalah, Chol Hamoed, or ordinary weekdays. A window more than a week out shows as scheduled (a banner) and only becomes active as it nears; it turns itself off automatically once the window has passed. It is mutually exclusive with guest mode.

Predictable. Because the whole transform is seeded, it's deterministic, the timeline preview shows exactly what will happen each night, and a backup instance simulates the identical pattern if it takes over.

Test mode (rehearse a Shabbos or Yom Tov)

Test mode runs the scheduler on a virtual clock so you can rehearse an upcoming Shabbos or Yom Tov on your real lights on any ordinary day. You pick the moment to pretend it is, and the app arms your actual schedule against that virtual time, so the real rules drive the real devices. It refuses to start while a real Shabbos or Yom Tov (or its erev) is already in effect, and it exits itself the moment a real one begins. On exit it puts every light back to the exact level it had when you started, because it snapshots them on entry.

Stepping through the actions. Instead of waiting hours for each rule to arrive, you step through the schedule one action at a time with the previous and next arrows. Each jump moves the virtual clock to a few seconds before that action (5 seconds by default, and you can change it), so you arrive first and then watch the light change fire on your real devices.

Why there is a short pause between steps. After each step the arrows are briefly disabled, for your pre-roll seconds plus a settle buffer of about 6 seconds (so roughly 11 seconds with the default). Every step actually drives your lights, and a slow hub or a scene that touches many devices needs a moment to finish adjusting. The pause lets one action play out fully before you can jump to the next, so steps do not stack up and flood the hub.

Guarding your own automations

SmartOneg drives the lights you put on its schedule, but it can also act as a Shabbos/Yom Tov gatekeeper for the rest of your smart home. GET /api/oneg answers whether Shabbos or Yom Tov is active, live, with no login required (one Oneg covers both Shabbos and Yom Tov):

curl http://YOUR-SMARTONEG:1836/api/oneg

{
  "active": true,
  "label": "Shabbos",
  "startsAt": "2026-08-07T23:38:00.000Z",
  "endsAt":   "2026-08-09T00:41:00.000Z",
  "now":      "2026-08-08T14:12:00.000Z"
}

active is true from candle lighting until havdalah (across a multi‑day Yom Tov too), and false otherwise, computed on‑device from the same zmanim SmartOneg schedules against, so it flips at the exact same moment. Put a check for it at the top of any weekday automation and bail out when it's true, and that automation simply won't run on Shabbos or Yom Tov.

Home Assistant

Add a REST binary sensor, then guard automations with a condition:

# configuration.yaml
binary_sensor:
  - platform: rest
    name: Shabbos or Yom Tov
    unique_id: smartoneg_oneg
    resource: http://YOUR-SMARTONEG:1836/api/oneg
    value_template: "{{ value_json.active }}"
    scan_interval: 60
# in an automation: only run when it's NOT Shabbos/Yom Tov
condition:
  - condition: state
    entity_id: binary_sensor.shabbos_or_yom_tov
    state: "off"

Apple HomeKit (Shortcuts)

HomeKit automations can't call a URL directly, so trigger a Shortcut (via a HomeKit automation, or the Home app's "Convert to Shortcut") that checks first:

  1. Get Contents of http://YOUR-SMARTONEG:1836/api/oneg
  2. Get Dictionary Value — key active
  3. If that value is 1 (true) → Stop This Shortcut. Put the rest of your actions after the If, so they only run when it's not Shabbos/Yom Tov.

Keyboard shortcuts

A few keys speed up the pages you use most. On macOS use ; on Windows/Linux use Ctrl.

HTTP API reference

All routes are under /api and return JSON. Browser sessions authenticate with a cookie (from POST /api/auth/login); automation can use a Bearer token (the failover.syncToken from your config) in the Authorization header. Below are the most useful endpoints.

Health & auth

Method & pathPurpose
GET /api/healthStatus, version, role, bridge connectivity (unauthenticated).
GET /api/onegIs Shabbos/Yom Tov in effect right now? { active, label, startsAt, endsAt, now } (unauthenticated). See Guarding automations.
POST /api/auth/login{ email, password } → session cookie.
POST /api/auth/logoutEnd the session.
GET /api/meCurrent account.

Devices & control

Method & pathPurpose
GET /api/zonesAll devices with expected/reported levels and latches.
PATCH /api/zones/:idRename, set room, device type, dimmable, enforce, °C/°F.
POST /api/zones/:id/command{ level, confirm? }, set a level (0–100).
POST /api/zones/:id/flash{ times }, blink as a reminder.
POST /api/zones/manualAdd a virtual device.
DELETE /api/zones/:idRemove (409 first with references; ?force=true cascades).
POST /api/zones/importIngest a Lutron integration report.
GET /api/devices/streamServer-sent events: live level changes.

Schedules & timeline

Method & pathPurpose
GET /api/schedulesAll schedules, keyed by day-type → variant.
GET /api/schedules/metaApplicable day-types and variants.
PUT /api/schedules/:dayType/:variantSave rules, inheritsRegular, removedIds.
GET /api/schedules/next-occurrencesNext date each situation occurs (up to 30 years).
GET /api/timeline?date=YYYY-MM-DDCompiled actions for that day (&guest=1 to force guest).
POST /api/compileRecompile; returns conflicts + report.
POST /api/guest-mode{ enabled }, toggle guest mode for the next cluster.
POST /api/away-mode{ enabled, from, to, label }, enable presence simulation over a date window (disable guest mode).
GET /api/away-presetsSuggested away windows (whole festivals + upcoming Shabbosos).

Scenes

Method & pathPurpose
GET /api/scenesAll scenes.
POST /api/scenes · PUT /api/scenes/:idCreate / update a scene.
POST /api/scenes/:id/previewApply live with a one-time snapshot.
DELETE /api/scene-previewExit preview and restore.

Calendar & PDFs

Method & pathPurpose
GET /api/calendar?from=&to=Clusters (candle lighting → havdalah) with day details.
GET /api/zmanim?date=Full zmanim for a date.
GET /api/pdf/yomtov/:festivalPrintable festival zmanim sheet.
GET /api/pdf/shabbos-yearFull-year Shabbos sheet.
GET /api/pdf/omerSefiras HaOmer chart.

Settings, backups & integrations

Method & pathPurpose
GET /api/settings · PUT /api/settingsRead / patch config (secrets redacted as __SET__).
GET /api/sync/export · POST /api/config/importFull config export / import.
GET /api/backups · POST /api/backups/:name/restoreList / restore nightly snapshots.
POST /api/homeassistant/{discover,import}Discover and import HA entities.
POST /api/homebridge/{discover,import}Homebridge accessories.
POST /api/matter/{commission,discover,import}Pair and import Matter devices.
POST /api/notify/testSend a test notification to every enabled channel.
GET /api/logs · GET /api/logs/downloadSearch the log ring / download the full log file.

From source (developers)

Developers, developers, developers
git clone https://github.com/moshechaikin/smart-oneg.git
cd smart-oneg
npm install                # all deps, including dev/test tooling
npm start                  # production server on http://localhost:1836
npm run dev                # same, but restarts on file changes (node --watch)

Data goes in ./data (override with DATA_DIR); change the port with PORT. After editing any Tailwind classes, rebuild the stylesheet:

npm run build:css          # public/css/tailwind.css → public/css/app.css (minified)

Tests

The repo ships a full suite, unit + full-stack integration (real scheduler, a mock Lutron bridge, the enforcement pipeline) plus end-to-end browser tests.

npm test                   # vitest: unit + integration (the main suite)
npm run test:watch         # vitest in watch mode
npm run test:e2e           # Playwright end-to-end (needs: npx playwright install)

Developed by Moshe Chaikin · Powered by Hebcal