← Blog
1.2 Release · 21 August 2026

What it writes, and what it claims

A beta is run on instances nobody building it can imagine, and it comes back with the faults that only happen there. Three of the changes below started as a report from someone running 1.1 against their own scale; the rest are the writing path being made to ask before it acts.

The import shows what it found before it writes

“Import from Home Assistant” used to read and write in one tap, and only then show the report — how many readings were new, the period they covered, the chart. The numbers arrived after the moment they could have been acted on, which is the wrong order for the one operation the sensor picker exists to guard: a year of the wrong sensor comes back out of Health one reading at a time.

The import now stops short of Health. It reads, it reports, and it writes on confirmation. Change sensors goes back to the picker and re-reads with the corrected mapping, and cancelling leaves nothing behind — nothing in the reading path touches Health or the record of what was written. What the preview counts is what gets written, because a reading already in Health is now recognised while reading rather than while writing.

Scheduled syncs are untouched. There is nobody to confirm anything to at four in the morning.

A year of it actually arrives

The statistics import in 1.1 was reading the right table and losing the answer. Home Assistant compiles a year of hourly buckets into a single WebSocket message of several megabytes, and iOS drops anything over a mebibyte — so the import fell back on the ten days of raw states without saying so. The ceiling now fits the reply. Against a live instance with a year on it: 180 new readings of 182, reaching back to the recorder’s own horizon rather than to last week.

A weigh-in its own neighbours contradict

An import wrote everything it read, including the morning somebody stepped on the scale holding something. That reading then stopped being a stray point and became the truth: the delta on the dashboard, the arrow on the widget, the range under the seven-day chart. Removing it meant knowing its date and finding it by hand.

A reading is now measured against the median of the readings around it, with the tolerance set by how far those scatter — medians on both sides, because an average and a standard deviation are each moved by the very point being judged. The thresholds were calibrated against a real year of readings rather than picked: a genuine three-kilogram change passes, and the two bad mornings in that year do not. The neighbourhood is counted in readings rather than in days, so the rule does not quietly switch itself off for someone who weighs twice a week.

Live syncs are deliberately left alone. With only the past to compare against, a real change looks exactly like a bad reading — on that same year, a true three-kilogram shift would have been refused nine times over the fortnight that followed it.

Unchanged for how long

“Unchanged since yesterday”, over a morning that had already synced. Three faults met in that one line. The verdict was really about whether the run had written anything, so a run that wrote nothing — Health refusing the type, a reading deleted once already, no reading from the scale — came out as unchanged and drew the green mark that means the number held steady. The caption then dated that non-event from the newest row in Health. And “unchanged since Tuesday” is heard as a claim about how fresh the data is.

The verdict is now decided by what the run learned: moved, unchanged, or undecided with the reason said out loud, and undecided wears neutral ink rather than green. The wording is a duration — “Unchanged for 3 days” — measured across the whole run of equal readings. And the morning is visible at last: a weigh-in landing on yesterday’s number to the decimal never changes the sensor’s value, but it does change when the sensor was last updated, so the app can say “Unchanged, seen 2 hours ago”. That timestamp is read and deliberately not written — an entry is stamped with the moment the reading changed, which is what stops a sensor repeating itself from earning a row per poll.

Nothing on your body is graded

From a report on 1.1: make the trend colours neutral, so as not to attach a meaning to gaining or losing weight. The hue never tracked direction — each metric simply had one colour of its own — but the palette was a traffic light and was read as one, body fat down 0.1% burning orange while muscle up 0.2 kg glowed green.

Deltas and arrows now take the reading’s own ink, the same tone whichever way they point. A metric’s hue is left to say only which metric a card is about, and those hues have moved off the warning colours: plum, amber, teal, and a blue of its own for weight. The accent keeps what is about the sync itself. The widget follows.

The first sign-in, and the row beneath it

The first connection failed with “The Internet connection appears to be offline” on working Wi-Fi, and the second one worked. That sentence is what iOS answers an app’s first request to a local address with while it raises the local network prompt — the request that raises the prompt has already failed by the time the prompt appears. Signing in itself sailed through, because the password is typed in Safari’s process and that already has permission; the failure landed on the token exchange, the first request the app makes for itself.

The app now knocks on the instance when Sign in is tapped, before the browser opens, so the prompt comes first and it comes over a request that can afford to be lost. Any answer at all counts as an answer, a refusal has to outlast the wait to stop a sign-in, and everything else falls through to the browser as before. When the permission really has been declined the error says so and offers the way into Settings, naming the row to look for: “Body Scale”.

The status row under the dial had a related habit. It read stored settings and nothing else, so an address and a token were enough to light the Home Assistant tile whatever had happened since — a sync refused a second earlier could sit two inches beneath three lit tiles and a green tick. It now reports the last real attempt and holds it until a call gets through, asks Health about every metric in the mapping rather than about weight alone, and re-reads when the app comes forward, so a permission switched off while the app was in the background is visible.

Also

Settings ends with a feedback row. It fills in the version, iOS, device, which metrics are mapped and how the last sync ended, and all of it is there to read in the composer before anything is sent. No entity ids go in — an entity id tends to carry somebody’s name. In a beta build the row above it points at the screenshot gesture, which is still the shortest path.

1.2 is a TestFlight build; the App Store listing is not open yet.

Bugs and requests: [email protected].