From a049c9d04f49a170ca94f2c3aaccab1e020c7d4e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 18:39:51 +0000 Subject: [PATCH] chore(release): version packages --- .changeset/cmd-log-on-the-box.md | 5 ----- .changeset/grid-phases-on-the-wire.md | 5 ----- .changeset/plan-battery-level.md | 5 ----- .changeset/watch-cloud-once.md | 5 ----- .changeset/watch-exchange.md | 5 ----- .changeset/watch-registry-names.md | 5 ----- .changeset/watch-role-enrolment.md | 5 ----- CHANGELOG.md | 15 +++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 10 files changed, 18 insertions(+), 38 deletions(-) delete mode 100644 .changeset/cmd-log-on-the-box.md delete mode 100644 .changeset/grid-phases-on-the-wire.md delete mode 100644 .changeset/plan-battery-level.md delete mode 100644 .changeset/watch-cloud-once.md delete mode 100644 .changeset/watch-exchange.md delete mode 100644 .changeset/watch-registry-names.md delete mode 100644 .changeset/watch-role-enrolment.md diff --git a/.changeset/cmd-log-on-the-box.md b/.changeset/cmd-log-on-the-box.md deleted file mode 100644 index d6a21dd48..000000000 --- a/.changeset/cmd-log-on-the-box.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": patch ---- - -An app command retried in a new session no longer acts twice. Each app session kept its own list of command ids, so when a phone lost its connection mid-command and sent the command again, the box acted again. The box now keeps one list for all sessions, keyed by the device and its command id, and holds each id for 24 hours of uptime, as the protocol says. Another device's command with the same id still counts as new. diff --git a/.changeset/grid-phases-on-the-wire.md b/.changeset/grid-phases-on-the-wire.md deleted file mode 100644 index 2a2c651f0..000000000 --- a/.changeset/grid-phases-on-the-wire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": patch ---- - -The box streams the grid's three phase currents to the app and to FTW Cloud, so the phone can show each phase against the main fuse. They go as extra fields 11–13 in tenths of an ampere, signed by the site convention, with the main fuse as field 14. A meter that gives only the size of each current, such as Sungrow, gets its sign from the power on the same phase. The box sends only the phases the site has, and only while the site meter is online. When a field stops coming, the box resends the whole snapshot once, so a phone never keeps a phase at its last value. Lane 0 keeps its 512-byte frame, and these fields are the first to give way when a delta does not fit. diff --git a/.changeset/plan-battery-level.md b/.changeset/plan-battery-level.md deleted file mode 100644 index 213e81e63..000000000 --- a/.changeset/plan-battery-level.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": minor ---- - -The plan an app reads (`plan.get` and the plan pushed after a command) now carries the battery's planned level at the end of each slot, `socPermille`, in thousandths: the same name and unit as the FTW Cloud plan class. A phone can then say where the battery ends up, not only what it does. A level the planner did not give as a number is left out. Older apps ignore the field. diff --git a/.changeset/watch-cloud-once.md b/.changeset/watch-cloud-once.md deleted file mode 100644 index ff1a1cf5f..000000000 --- a/.changeset/watch-cloud-once.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": patch ---- - -Specify how an Apple Watch reads its home from FTW Cloud in one HTTPS request (ADR 0009). `go/protocol/cloudwire` gains the one-shot read: `POST /v1/app/once`, a Noise IK handshake with prologue `ftw.cloud.once.v1:`, `read {have, hz}` and `token.get` in message 1, and an answer of length-prefixed messages. `push.register` gains `app`, `iphone` or `watch`, so the service picks the APNs topic, and the roster may carry a device with role `watch`. Golden vectors for one read are in `cloudwire/testdata/cloud-once-vectors.json`. Nothing on the box changes; FTW Cloud serves the route in a later step. diff --git a/.changeset/watch-exchange.md b/.changeset/watch-exchange.md deleted file mode 100644 index 196d6655a..000000000 --- a/.changeset/watch-exchange.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": minor ---- - -An Apple Watch can charge the car and read the house without its phone (ADR 0009). The box keeps a second relay socket, `/r///exchange`, beside its uplink, and answers a watch's one-shot HTTPS requests on it: a Noise IK handshake with prologue `ftw.exchange.v1:` that carries the watch's hello and the box's `hello_ok`, then `cmd` and `now`. Watch sessions live in a table of eight apart from the phones' four, so a watch never pushes a phone out, and each ends after four exchanges, a minute idle or three minutes. Every answer is two lane 0 frames, so a read, a command and a refusal have one length; a command's result that has not settled in 3 s leaves a tick in its place, and asking again with the same `cmdId` returns it. The box answers a command no sooner than 400 ms after it arrived, applied or refused, so the time to answer does not tell the relay which; a command that takes longer answers when it is done. The box takes at most 60 handshakes a minute and answers a stranger or a removed watch with silence. It now says `cmd.exchange`, so a phone can offer "Add watch". Losing the exchange socket changes nothing for the uplink or local control. diff --git a/.changeset/watch-registry-names.md b/.changeset/watch-registry-names.md deleted file mode 100644 index bed15c234..000000000 --- a/.changeset/watch-registry-names.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": patch ---- - -Know the shared registry's new names for an Apple Watch: role `watch` and capability `cmd.exchange` (ADR 0009). The box claims neither yet. Its page and the app refuse a watch code, and refuse to change a phone into a watch, until a phone can add one. diff --git a/.changeset/watch-role-enrolment.md b/.changeset/watch-role-enrolment.md deleted file mode 100644 index 854f30b83..000000000 --- a/.changeset/watch-role-enrolment.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ftw": patch ---- - -An owner's phone can add an Apple Watch as a device of its own (ADR 0009). The phone asks for a watch code over its session, with a passkey step-up on that request; the code binds to the phone and leaves a pending phone code alone. The box stores the watch with role `watch` and the phone as its parent. A watch holds the watch role's scopes that its phone holds at that moment, so demoting the phone narrows the watch at once. Removing a phone removes its watches; removing a watch touches nothing else. A watch never gets a stream session. The box's page and `GET /api/app-link/devices` list each watch under its phone, and FTW Cloud's roster carries it with the scopes the box derives. The box does not offer the watch path yet: `cmd.exchange` stays unsaid until the exchange socket lands. diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ec3a5db..bba6245a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.143.0 + +### Minor Changes + +- 6e05ac5: The plan an app reads (`plan.get` and the plan pushed after a command) now carries the battery's planned level at the end of each slot, `socPermille`, in thousandths: the same name and unit as the FTW Cloud plan class. A phone can then say where the battery ends up, not only what it does. A level the planner did not give as a number is left out. Older apps ignore the field. +- 7c72a5b: An Apple Watch can charge the car and read the house without its phone (ADR 0009). The box keeps a second relay socket, `/r///exchange`, beside its uplink, and answers a watch's one-shot HTTPS requests on it: a Noise IK handshake with prologue `ftw.exchange.v1:` that carries the watch's hello and the box's `hello_ok`, then `cmd` and `now`. Watch sessions live in a table of eight apart from the phones' four, so a watch never pushes a phone out, and each ends after four exchanges, a minute idle or three minutes. Every answer is two lane 0 frames, so a read, a command and a refusal have one length; a command's result that has not settled in 3 s leaves a tick in its place, and asking again with the same `cmdId` returns it. The box answers a command no sooner than 400 ms after it arrived, applied or refused, so the time to answer does not tell the relay which; a command that takes longer answers when it is done. The box takes at most 60 handshakes a minute and answers a stranger or a removed watch with silence. It now says `cmd.exchange`, so a phone can offer "Add watch". Losing the exchange socket changes nothing for the uplink or local control. + +### Patch Changes + +- 9fa327f: An app command retried in a new session no longer acts twice. Each app session kept its own list of command ids, so when a phone lost its connection mid-command and sent the command again, the box acted again. The box now keeps one list for all sessions, keyed by the device and its command id, and holds each id for 24 hours of uptime, as the protocol says. Another device's command with the same id still counts as new. +- 5c1feae: The box streams the grid's three phase currents to the app and to FTW Cloud, so the phone can show each phase against the main fuse. They go as extra fields 11–13 in tenths of an ampere, signed by the site convention, with the main fuse as field 14. A meter that gives only the size of each current, such as Sungrow, gets its sign from the power on the same phase. The box sends only the phases the site has, and only while the site meter is online. When a field stops coming, the box resends the whole snapshot once, so a phone never keeps a phase at its last value. Lane 0 keeps its 512-byte frame, and these fields are the first to give way when a delta does not fit. +- 5d2e7fe: Specify how an Apple Watch reads its home from FTW Cloud in one HTTPS request (ADR 0009). `go/protocol/cloudwire` gains the one-shot read: `POST /v1/app/once`, a Noise IK handshake with prologue `ftw.cloud.once.v1:`, `read {have, hz}` and `token.get` in message 1, and an answer of length-prefixed messages. `push.register` gains `app`, `iphone` or `watch`, so the service picks the APNs topic, and the roster may carry a device with role `watch`. Golden vectors for one read are in `cloudwire/testdata/cloud-once-vectors.json`. Nothing on the box changes; FTW Cloud serves the route in a later step. +- 00d791c: Know the shared registry's new names for an Apple Watch: role `watch` and capability `cmd.exchange` (ADR 0009). The box claims neither yet. Its page and the app refuse a watch code, and refuse to change a phone into a watch, until a phone can add one. +- a6ddfea: An owner's phone can add an Apple Watch as a device of its own (ADR 0009). The phone asks for a watch code over its session, with a passkey step-up on that request; the code binds to the phone and leaves a pending phone code alone. The box stores the watch with role `watch` and the phone as its parent. A watch holds the watch role's scopes that its phone holds at that moment, so demoting the phone narrows the watch at once. Removing a phone removes its watches; removing a watch touches nothing else. A watch never gets a stream session. The box's page and `GET /api/app-link/devices` list each watch under its phone, and FTW Cloud's roster carries it with the scopes the box derives. The box does not offer the watch path yet: `cmd.exchange` stays unsaid until the exchange socket lands. + ## 0.142.2 ### Patch Changes diff --git a/package-lock.json b/package-lock.json index 3753f7128..aa67a3bc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ftw", - "version": "0.142.2", + "version": "0.143.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ftw", - "version": "0.142.2", + "version": "0.143.0", "license": "SEE LICENSE IN LICENSE", "devDependencies": { "@changesets/cli": "^3.0.3" diff --git a/package.json b/package.json index 407c4f964..24f8bb672 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ftw", - "version": "0.142.2", + "version": "0.143.0", "private": true, "type": "module", "description": "FTW — local-first home energy coordination. Version metadata only; the runtime is Go and the mathematical planner is the compiled Energyplan worker.",