Skip to content

Releases: LTimothy/nightstand

3.3.0

3.3.0 Pre-release
Pre-release

Choose a tag to compare

@LTimothy LTimothy released this 27 Sep 00:29
  • The Status page has a Water tank entry. The pod reports its tank sensor on
    every status read, and that reading only ever showed on the temperature page
    while the tank was low. A low tank now counts as needing attention, which
    also lights the dot on the Status tab, and the entry shows when the tank went
    low. A change has to hold for about 30 seconds before it counts, so water
    moving during priming does not register.

  • Raw sensor recordings are kept for 14 days instead of 36 hours, and Settings
    can set anywhere from 2 days to 2 months. The recordings take about 0.4 GB a
    day. If the data partition drops below 2 GB free, the oldest recordings are
    removed first.

  • The server and the biometrics stream now run with memory limits, and their
    processes are the first the system stops if memory runs out. The pod's
    heating and cooling firmware shares about 1.9 GB of memory with them, so a
    runaway analysis job should not be able to take memory it needs. The limits
    are installed by this update and apply from the next time each service
    starts. Reverting to stock removes them.

  • Settings no longer links to community chat or donation pages. Nightstand is
    maintained independently of the projects it builds on; for help, open an
    issue on this repository.

  • The guides were checked against the current code and corrected where they
    had drifted, most of all the API reference.

v3.2.2

v3.2.2 Pre-release
Pre-release

Choose a tag to compare

@LTimothy LTimothy released this 26 Sep 05:00
  • The pump warning on the Status page no longer goes off when a side is
    switched off. It raised "pump stall suspected" almost every day, because a
    side the schedule has switched off reports its pump at 0 rpm, and nothing else
    the pump reports tells a switched-off side from a stalled one. It now checks
    whether the side is switched on before calling a stopped pump a stall. A stall
    warning already showing when a side switches off now clears, where before it
    stayed until the pump ran again.

  • The Logs page opens large log files. It read a whole file before showing any
    of it, and rotated logs reach 15 MB, so on a busy pod the page could wait long
    enough for the browser to give up. It now reads only the end of the file,
    which is all the page shows.

v3.2.1

v3.2.1 Pre-release
Pre-release

Choose a tag to compare

@LTimothy LTimothy released this 24 Sep 17:17
  • When an update cannot apply its database changes, the Status page now says
    so. The database entry names the changes that are missing, and the Versions
    page offers Reinstall on the running version, which applies them. Before
    this, the server kept running without tables it needed, and nothing reported
    why until something that used them failed.

  • Updates now apply database changes whenever the database is behind, not only
    when the new version brings changes of its own. An update that left changes
    unapplied can now be finished by reinstalling the same version, which was
    not possible before.

  • The version being installed now finishes its own update. Updates were always
    run by the updater already on the pod, so a fix to the updater reached a pod
    one update after the one that delivered it. From the next update on, the new
    version's updater takes over once it has been downloaded. An update only
    hands over to an updater that supports this, so installing an older version
    still installs the version asked for.

    This starts with the update after this one. Installing 3.2.1 is still run by
    the updater already on the pod. Coming from 3.0 or 3.1, that updater can
    leave database changes unapplied, and if it does, the Status page will say so
    and one reinstall finishes them.

v3.2.0

v3.2.0 Pre-release
Pre-release

Choose a tag to compare

@LTimothy LTimothy released this 24 Sep 15:29
  • Calibration now needs the whole bed to be empty, not only the side being
    calibrated. It picked its quiet stretch by looking at its own side alone, so a
    side could calibrate while someone lay on the other one and measure their
    movement coming through the mattress instead of an empty bed. A stretch is now
    skipped if either side recorded a heart rate during it. When the bed was busy
    the whole time, calibration waits for another day rather than settling for the
    least busy stretch.

  • The calibration quality score now drops when the window it learned from was
    thin. Every second was being counted twice, so a window missing half its
    readings scored the same as a full one. Calibration also measures each side's
    signal level on an empty bed and keeps that with every run. It is recorded
    only, and does not change how presence is detected yet.

  • An update could leave the server running without database tables it needs.
    The database step ran while the biometrics service was still writing to the
    same file and could not get the lock it needed. That was logged as a warning,
    and the check after the update passed anyway, because it confirms the server
    answers, reports the right version and reads a sensor, and none of that
    touches the new tables. On one pod this left calibration failing every night
    until it was fixed by hand. Updates now pause the biometrics service while the
    database is updated, retry, confirm nothing is left pending, and roll back if
    it still did not apply. This protects updates made from this version on. The
    update that installs this version runs the updater already on the pod, so it
    does not get the fix itself.

  • Each time presence starts, the minute that follows is now recorded along with
    the few seconds before it. A person settles well above the level that starts a
    session, while an empty side that briefly crossed it drops back within
    seconds, so the log now shows which sessions were real without keeping raw
    sensor recordings around.

  • scripts/setup_watchdog.sh turns on the pod's hardware watchdog, so a frozen
    system restarts itself within about 30 seconds. On one pod a fault in the
    stock Wi-Fi driver froze the system partway through its nightly restart, and
    it stayed down, with no server and no cooling, until it was unplugged. Updates
    do not run this script. It is run once, as root, on the pod.

v3.1.0

v3.1.0 Pre-release
Pre-release

Choose a tag to compare

@LTimothy LTimothy released this 07 Aug 16:22
  • The Status page now shows when the active presence calibration profile was
    created and what it learned from, instead of only whether the last run
    succeeded. Calibration results are stored with the window they came from,
    so a thin result can be told apart from a good one. A pod that has never
    calibrated now says so plainly rather than reporting an error.

  • The in-bed indicator starts and ends far more sessions than anyone actually
    has. Over eleven days of recordings the live detector counted 23 to 30
    separate sessions per side per day, most of them under twenty minutes, while
    the overnight analysis of the same nights found the one real session per side
    you would expect. Signal strength is not the reason: an occupied side reads
    around forty times higher than an empty one, so the two are easy to tell
    apart. The sessions are being started by something in the entry logic, which
    the once-a-minute log is too coarse to show. Each start is now recorded along
    with the few seconds that led into it, so the next look at this can read what
    happened instead of estimating.

  • The pump warning on the Status page is a false alarm, and this release starts
    gathering what is needed to fix it properly. The check treats "pump reporting
    no speed while the cooling element draws current" as a stalled pump, but
    across eleven days of recordings the pump reports no speed for exactly the
    hours the power schedule has that side switched off, and the current reading
    never drops low enough to tell a switched-off side from a running one. So the
    warning fires most days when the bed powers off. The check is unchanged for
    now, because getting it wrong in the other direction would hide a real stall.
    It now records the full pump reading when the pump starts or stops reporting
    speed, which is the missing piece for telling those two cases apart.

  • The heart rate, HRV and breathing charts on the Sleep page were blank. The
    server reformatted each reading's timestamp into a local-time string before
    sending it, and the charts scale the timestamp themselves and discard
    anything that is not a number, so every reading was thrown away and the
    charts drew nothing. No error appeared anywhere. Readings now go out as the
    plain timestamps they are stored as, which is what the charts already expect.

  • Heart rate variability and breathing rate recorded at the start of a sleep
    session belonged to the previous session. Both are smoothed running values,
    and neither can be recomputed immediately: breathing rate needs 30 seconds of
    established presence and HRV needs five minutes. Leaving the bed cleared the
    samples behind them but not the values themselves, so the opening minutes of
    the next session were written with whoever was there last. Measured against
    eleven days of recordings, that was 16% of stored readings, 10% of them
    carrying a plausible-looking number rather than the blank the rest of the
    system knows to ignore. Leaving the bed now clears both, along with any
    measurement still waiting to be written.

  • A physical double or triple tap that failed to write its temperature change
    restarted the server. Tap handling runs detached from the polling loop, so a
    base movement over Bluetooth cannot delay the next tap being noticed, but that
    also meant a failure had nowhere to go and the server treats an unhandled one
    as a reason to shut down. The failure is now caught where it happens, logged,
    and shown on the Status page.

v3.0.1

v3.0.1 Pre-release
Pre-release

Choose a tag to compare

@LTimothy LTimothy released this 02 Aug 05:03

A bug-fix release. Most of it comes from one root cause: the app and the
server disagreed about which day a schedule ends on. The app asked whether
the off time falls before the on time, which is right. The server used a
fixed rule that a time at or before noon belongs to the next day, and never
looked at the on time at all. The presence monitor carried a third copy of
that rule. All three now share one model: a day's schedule opens at its
power-on time, so a time at or after that belongs to the same day and
anything earlier falls on the next one.

Fixed in the schedulers:

  • A side could stay on for days. A Saturday 23:00 to 13:00 schedule put the
    power-off ten hours before its own power-on, so the next one to actually
    run was the following Saturday. A morning nap or an after-midnight
    schedule had smaller versions of the same problem.
  • Alarms took their day from the power-off time rather than from the alarm's
    own time, so changing only when the bed switches off could move the alarm
    to a different weekday. The alarm then found the side already off and
    skipped itself, which looked like no alarm at all.
  • Temperature adjustments could scatter one night's changes across three
    days, since each entry applied the old rule to a different time.
  • An alarm saved without a time passed validation and then threw while being
    scheduled. Because every job was cancelled before the rebuild, one bad
    entry could leave the pod with no power, temperature, alarm, or priming
    jobs, and it stayed that way across restarts. Schedules are now validated
    more strictly, an unusable alarm is skipped rather than fatal, and each day
    is scheduled on its own so one failure cannot take down the rest.
  • An alarm override accepted any text. "25:00" quietly armed the alarm for
    01:00 the next day. Override times and dates are now validated.
  • A slow clock sync at boot could leave the pod with no jobs for the life of
    the process, because the retry budget ran out after 100 seconds and stopped
    for good. It now keeps retrying on a longer interval.
  • The daily reboot silently never scheduled when priming was set before
    01:00, because the hour worked out to -1.
  • Powering on no longer overwrites a temperature you set by hand while the
    temperature schedule is paused.

Fixed in presence auto-off:

  • Auto-off treated "no presence data" as "nobody there" and could switch a
    side off with someone in it, 45 minutes after the biometrics stream stopped
    reporting. Turning biometrics off in settings stops that stream, so this
    was reachable from the app. Presence is now three states, and auto-off
    holds when it is unknown rather than assuming an empty bed.
  • A clock correction after boot read as hours of absence and switched a side
    off on the next check.

Fixed in the app:

  • Dismissing a vibrating alarm latched the dialog closed, so the next alarm
    did not show one.
  • Several controls kept an optimistic value after the save failed, showing a
    temperature, power state, or away-mode setting the pod never accepted.
  • The sleep chart labelled times like "22:30pm" and could put the wrong
    weekday under a bar.
  • A schedule with alarms saved in the older single-alarm shape always looked
    edited, so discard never went quiet.
  • A malformed settings response could blank the whole app instead of one
    section, and an invalid live-update frame could write over the cached
    device status and show the bed as off.

Also in this release:

  • Settings now links to Software and updates, which was built and routed but
    had no way in. Its version picker and instant rollback were gated behind a
    version floor that no release had reached, so both were unavailable; the
    floor now sits at 3.0.0, where the features it guards actually shipped.

Known limitation, not fixed here: on the spring daylight-saving change, a job
scheduled in the hour that does not exist that day is skipped, and a weekly
job skips a full week rather than a day. The autumn case, where an alarm
could fire twice, is fixed. Addressing the spring case means replacing the
recurrence rules with explicit per-day scheduling, which is a larger change
to safety-critical code than belongs in a patch release.

v3.0.0

Choose a tag to compare

@LTimothy LTimothy released this 10 Jul 18:43

[3.0.0] - 2026-07-08

Initial release of Nightstand's own version stream, a hard fork descending from
jmew/free-sleep (presence detection, sleep stages, adjustable-base BLE control,
WebSocket live updates) and throwaway31265/free-sleep before it.

Added

  • Own semver stream independent of upstream, with upstreamBase tracked in
    serverInfo.json for deliberate, reviewed cherry-picking instead of
    auto-merging.
  • In-app updater: the Settings Update button pulls new builds directly from this
    project's GitHub, with backup, health check, and automatic rollback on failure.
  • RAW-file archive so overnight biometrics survive the firmware's ~75-minute
    rolling-buffer truncation, and LAN deploy tooling (ops/deploy.sh,
    ops/rollback.sh) with backup and rollback for development and emergencies.
  • Manual power-on now starts at the scheduled temperature, applying the
    temperature the schedule would have reached by now. Adapted from
    EpicPi/free-sleep. Temperature display respects the stored Fahrenheit or level
    format instead of hardcoding one.
  • App and Python test runners, and a hosted mocked demo deployed to GitHub Pages
    on every push so the app can be tried in a browser without a Pod.

Fixed

  • Pod 5 biometrics: RAW records are no longer skipped. cbor2's C extension
    read in 4096-byte chunks, so nearly every ~2700-byte Pod 5 record was silently
    dropped and empty placeholder records ended the read loop early. Records are
    now parsed with a byte-accurate reader, adapted from the original project's PRs
    #46 and #50.
  • Adjustable-base page goes dormant instead of logging retry errors forever when
    no base is configured, and the sleep-stage classifier requires HR and HRV
    evidence for REM instead of defaulting to it.
  • Presence auto-off respects the user's power schedule instead of powering the
    bed down mid-sleep.

Removed

  • Sentry error-reporting integration; its DSNs pointed at an account this fork
    can't read, so reports were write-only.