From 80c0e955f0db4eefe545dcc723a22e7ed5ac65f2 Mon Sep 17 00:00:00 2001 From: bonamin Date: Mon, 21 Sep 2026 20:35:03 +0300 Subject: [PATCH] fix(devices): zero out unassigned bell outputs when saving Bell Mechanisms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit relay.set_config is a partial update on the firmware side — any bell index not present in the durations/outputs maps is left completely untouched. EditBellOutputsModal and OnboardDevice's bell config step both only sent entries for the currently-active bell count, so deselecting a bell (lowering the active count) never actually deactivated its output on the device — it stayed wired to whatever it was last set to. The only way to disable a bell was to reselect it and explicitly set it to "Disabled", which isn't obvious. Both now send all maxOutputs slots on every save, forcing output to 0 for anything beyond the active count, so lowering the active bell count reaches the firmware the way it visually appears to in the UI. Co-Authored-By: Claude Sonnet 5 --- .../bellcloud/devices/EditBellOutputsModal.jsx | 12 ++++++++++-- .../src/pages/bellcloud/devices/OnboardDevice.jsx | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/src/modals/bellcloud/devices/EditBellOutputsModal.jsx b/frontend/src/modals/bellcloud/devices/EditBellOutputsModal.jsx index 6a73038..f8ea12a 100644 --- a/frontend/src/modals/bellcloud/devices/EditBellOutputsModal.jsx +++ b/frontend/src/modals/bellcloud/devices/EditBellOutputsModal.jsx @@ -51,11 +51,19 @@ export default function EditBellOutputsModal({ open, onClose, onSaved, attr, sub // Push to the device first — relay.set_config takes both maps in one call. // Only write to Firestore once the device confirms it actually applied // and persisted the change (sendMqttCommand's toast reports the outcome). + // + // relay.set_config is a partial update — any bell index NOT present in + // these maps is left untouched by the firmware. So every slot beyond + // activeBells must be sent explicitly as output 0 (disabled), or a bell + // that was previously active stays wired to its old output forever, + // even though it's no longer shown as active here. The only way to + // deactivate a bell today is to select it and set it to Disabled — this + // makes "deselect it" do the same thing implicitly. const durations = {} const outputs = {} - activeRows.forEach((r, i) => { + rows.forEach((r, i) => { durations[i] = r.timing - outputs[i] = r.output + outputs[i] = i < activeBells ? r.output : 0 }) await sendMqttCommand('relay.set_config', { durations, outputs }) diff --git a/frontend/src/pages/bellcloud/devices/OnboardDevice.jsx b/frontend/src/pages/bellcloud/devices/OnboardDevice.jsx index dc71877..b3fb292 100644 --- a/frontend/src/pages/bellcloud/devices/OnboardDevice.jsx +++ b/frontend/src/pages/bellcloud/devices/OnboardDevice.jsx @@ -1275,9 +1275,13 @@ function ConfigureStep({ device, onDeviceUpdated, onNext, onBack }) { // ── Build the command sequence + Firestore patch for each group ───────── const buildGroups = useCallback(() => { const activeBellRows = bell.rows.slice(0, bell.activeBells) + // relay.set_config is a partial update — any bell index NOT present in + // these maps is left untouched by the firmware. Send every row (not just + // the active ones) so anything beyond activeBells is explicitly zeroed — + // same fix as EditBellOutputsModal. const durations = {} const outputs = {} - activeBellRows.forEach((r, i) => { durations[i] = r.timing; outputs[i] = r.output }) + bell.rows.forEach((r, i) => { durations[i] = r.timing; outputs[i] = i < bell.activeBells ? r.output : 0 }) return [ {