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 [ {