fix: hold simulation only for shades a command starts moving

markTxPending ran inside sendCommandAfter before callers set the new
target, so isAtTarget() kept idle shades from ever being held, while
shades already moving were frozen although their motor kept running.

Hold a shade only when it was idle before the command and release the
hold when the command didn't set it moving.  The single mark in
sendCommandAfter now covers group members too, so the second pass in
SomfyGroup::sendCommand is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-11 12:46:39 +02:00
parent 2a54740f52
commit 130b5b6b0a

View file

@ -1144,6 +1144,10 @@ void SomfyShade::checkMovement() {
uint8_t currPos = floor(this->currentPos); uint8_t currPos = floor(this->currentPos);
uint8_t currTiltPos = floor(this->currentTiltPos); uint8_t currTiltPos = floor(this->currentTiltPos);
if(this->direction != 0) this->lastMovement = this->direction; if(this->direction != 0) this->lastMovement = this->direction;
if(this->txPendingSeq != 0 && this->direction == 0 && this->tiltDirection == 0) {
// The command didn't set the shade moving so there is nothing to hold.
this->txPendingSeq = 0;
}
if(this->txPendingSeq != 0) { if(this->txPendingSeq != 0) {
// The command that set us moving hasn't gone on air yet so the motor hasn't // The command that set us moving hasn't gone on air yet so the motor hasn't
// started. Hold the simulated position until it does. // started. Hold the simulated position until it does.
@ -2817,8 +2821,13 @@ void SomfyShade::setTiltMovement(int8_t dir) {
} }
} }
void SomfyShade::markTxPending(uint32_t seq) { void SomfyShade::markTxPending(uint32_t seq) {
// Only a shade the command set moving has to wait for it. // The motor starts on the first of several queued commands to go on air.
if(this->isAtTarget()) return; if(this->txPendingSeq != 0) return;
// This runs before the caller sets the new target so direction still says
// whether the shade was moving. A moving motor carries on with its earlier
// command so its simulation must not be held. A reversal is simulated as
// soon as it is queued even though the motor turns once it is on air.
if(this->direction != 0 || this->tiltDirection != 0) return;
this->txPendingSeq = seq; this->txPendingSeq = seq;
this->txPendingSince = millis(); this->txPendingSince = millis();
} }
@ -3129,8 +3138,6 @@ bool SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
} }
} }
} }
// The member shades only started moving above.
if(this->lastTxSeq != 0 && startsMovement(this->lastFrame.cmd)) somfy.markTxPending(this->getRemoteAddress(), this->lastTxSeq);
this->updateFlags(); this->updateFlags();
this->emitState(); this->emitState();
return true; return true;
@ -4027,8 +4034,8 @@ bool SomfyRemote::sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t s
this->lastFrame = frame; this->lastFrame = frame;
this->lastTxSeq = seq; this->lastTxSeq = seq;
somfy.processFrame(this->lastFrame, true); somfy.processFrame(this->lastFrame, true);
// Shades this set moving wait for the frame to go on air. Stops are sent // Idle shades (and group members) this sets moving wait for the frame to go
// ahead of everything else so they don't need to. // on air. Stops are sent ahead of everything else so they don't need to.
if(seq != 0 && !priority && startsMovement(frame.cmd)) somfy.markTxPending(frame.remoteAddress, seq); if(seq != 0 && !priority && startsMovement(frame.cmd)) somfy.markTxPending(frame.remoteAddress, seq);
return true; return true;
} }