mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
fix: propagate radio TX result to callers instead of pre-checking
- Send functions return whether the radio took the command. - Shade, group and tilt state only change when the command was queued. - Rolling code is committed only after the frame is queued. - repeatFrame advances the repeat counter only on success. - Remove canTransmit/canQueueTx; queueTx is the single authority. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a426efe53f
commit
ad15ecacd4
2 changed files with 129 additions and 125 deletions
217
src/Somfy.cpp
217
src/Somfy.cpp
|
|
@ -2894,8 +2894,8 @@ void SomfyShade::setMyPosition(int8_t pos, int8_t tilt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SomfyShade::moveToMyPosition() {
|
bool SomfyShade::moveToMyPosition() {
|
||||||
if(!this->isIdle()) return;
|
if(!this->isIdle()) return true;
|
||||||
ESP_LOGI(TAG, "Moving to My Position");
|
ESP_LOGI(TAG, "Moving to My Position");
|
||||||
if(this->tiltType == tilt_types::tiltonly) {
|
if(this->tiltType == tilt_types::tiltonly) {
|
||||||
this->p_currentPos(100.0f);
|
this->p_currentPos(100.0f);
|
||||||
|
|
@ -2903,27 +2903,25 @@ void SomfyShade::moveToMyPosition() {
|
||||||
}
|
}
|
||||||
if(this->currentPos == this->myPos) {
|
if(this->currentPos == this->myPos) {
|
||||||
if(this->tiltType != tilt_types::none) {
|
if(this->tiltType != tilt_types::none) {
|
||||||
if(this->currentTiltPos == this->myTiltPos) return; // Nothing to see here since we are already here.
|
if(this->currentTiltPos == this->myTiltPos) return true; // Nothing to see here since we are already here.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
if(this->myPos == -1 && (this->tiltType == tilt_types::none || this->myTiltPos == -1)) return;
|
if(this->myPos == -1 && (this->tiltType == tilt_types::none || this->myTiltPos == -1)) return true;
|
||||||
if(this->tiltType != tilt_types::tiltonly && this->myPos >= 0.0f && this->myPos <= 100.0f) this->p_target(this->myPos);
|
|
||||||
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
|
|
||||||
this->settingPos = false;
|
this->settingPos = false;
|
||||||
if(this->simMy()) {
|
if(this->simMy()) {
|
||||||
ESP_LOGI(TAG, "Moving to simulated favorite");
|
ESP_LOGI(TAG, "Moving to simulated favorite");
|
||||||
this->moveToTarget(this->myPos, this->myTiltPos);
|
if(!this->moveToTarget(this->myPos, this->myTiltPos)) return false;
|
||||||
}
|
}
|
||||||
else
|
else if(!SomfyRemote::sendCommand(somfy_commands::My, this->repeats)) return false;
|
||||||
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
if(this->tiltType != tilt_types::tiltonly && this->myPos >= 0.0f && this->myPos <= 100.0f) this->p_target(this->myPos);
|
||||||
|
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
bool SomfyShade::sendCommand(somfy_commands cmd) { return this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
bool SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
||||||
ESP_LOGD(TAG, "Send command start");
|
ESP_LOGD(TAG, "Send command start");
|
||||||
// Don't move the simulated state for a command the radio can't take.
|
|
||||||
if(!this->canTransmit()) return;
|
|
||||||
// This sendCommand function will always be called externally. sendCommand at the remote level
|
// This sendCommand function will always be called externally. sendCommand at the remote level
|
||||||
// is expected to be called internally when the motor needs commanded.
|
// is expected to be called internally when the motor needs commanded.
|
||||||
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
||||||
|
|
@ -2932,21 +2930,22 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
|
||||||
if((cmd == somfy_commands::Up && this->direction == -1) ||
|
if((cmd == somfy_commands::Up && this->direction == -1) ||
|
||||||
(cmd == somfy_commands::Down && this->direction == 1)) {
|
(cmd == somfy_commands::Down && this->direction == 1)) {
|
||||||
ESP_LOGD(TAG, "Same command as dir");
|
ESP_LOGD(TAG, "Same command as dir");
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
if(!SomfyRemote::sendCommand(cmd, repeat)) return false;
|
||||||
this->p_target(this->currentPos);
|
this->p_target(this->currentPos);
|
||||||
this->p_tiltTarget(this->currentTiltPos);
|
this->p_tiltTarget(this->currentTiltPos);
|
||||||
this->setMovement(0);
|
this->setMovement(0);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
// The simulated state is only moved for commands the radio took.
|
||||||
if(cmd == somfy_commands::Up) {
|
if(cmd == somfy_commands::Up) {
|
||||||
if(this->tiltType == tilt_types::euromode) {
|
if(this->tiltType == tilt_types::euromode) {
|
||||||
// In euromode we need to long press for 2 seconds on the
|
// In euromode we need to long press for 2 seconds on the
|
||||||
// up command.
|
// up command.
|
||||||
SomfyRemote::sendCommand(cmd, TILT_REPEATS);
|
if(!SomfyRemote::sendCommand(cmd, TILT_REPEATS)) return false;
|
||||||
this->p_target(0.0f);
|
this->p_target(0.0f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
if(!SomfyRemote::sendCommand(cmd, repeat)) return false;
|
||||||
if(this->tiltType == tilt_types::tiltonly) {
|
if(this->tiltType == tilt_types::tiltonly) {
|
||||||
this->p_target(100.0f);
|
this->p_target(100.0f);
|
||||||
this->p_tiltTarget(0.0f);
|
this->p_tiltTarget(0.0f);
|
||||||
|
|
@ -2960,11 +2959,11 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
|
||||||
if(this->tiltType == tilt_types::euromode) {
|
if(this->tiltType == tilt_types::euromode) {
|
||||||
// In euromode we need to long press for 2 seconds on the
|
// In euromode we need to long press for 2 seconds on the
|
||||||
// down command.
|
// down command.
|
||||||
SomfyRemote::sendCommand(cmd, TILT_REPEATS);
|
if(!SomfyRemote::sendCommand(cmd, TILT_REPEATS)) return false;
|
||||||
this->p_target(100.0f);
|
this->p_target(100.0f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
if(!SomfyRemote::sendCommand(cmd, repeat)) return false;
|
||||||
if(this->tiltType == tilt_types::tiltonly) {
|
if(this->tiltType == tilt_types::tiltonly) {
|
||||||
this->p_target(100.0f);
|
this->p_target(100.0f);
|
||||||
this->p_tiltTarget(100.0f);
|
this->p_tiltTarget(100.0f);
|
||||||
|
|
@ -2975,43 +2974,45 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(cmd == somfy_commands::My) {
|
else if(cmd == somfy_commands::My) {
|
||||||
if(this->isToggle() || this->shadeType == shade_types::drycontact)
|
if(this->isToggle() || this->shadeType == shade_types::drycontact) {
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
if(!SomfyRemote::sendCommand(cmd, repeat)) return false;
|
||||||
|
}
|
||||||
else if(this->shadeType == shade_types::drycontact2){
|
else if(this->shadeType == shade_types::drycontact2){
|
||||||
ESP_LOGD(TAG, "Send command start 1");
|
ESP_LOGD(TAG, "Send command start 1");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
else if(this->isIdle()) {
|
else if(this->isIdle()) {
|
||||||
this->moveToMyPosition();
|
bool sent = this->moveToMyPosition();
|
||||||
ESP_LOGD(TAG, "Send command end 2");
|
ESP_LOGD(TAG, "Send command end 2");
|
||||||
return;
|
return sent;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
if(!SomfyRemote::sendCommand(cmd, repeat)) return false;
|
||||||
if(this->tiltType != tilt_types::tiltonly) this->p_target(this->currentPos);
|
if(this->tiltType != tilt_types::tiltonly) this->p_target(this->currentPos);
|
||||||
this->p_tiltTarget(this->currentTiltPos);
|
this->p_tiltTarget(this->currentTiltPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(cmd == somfy_commands::Toggle) {
|
else if(cmd == somfy_commands::Toggle) {
|
||||||
if(this->bitLength != 80) SomfyRemote::sendCommand(somfy_commands::My, repeat, stepSize);
|
if(this->bitLength != 80) { if(!SomfyRemote::sendCommand(somfy_commands::My, repeat, stepSize)) return false; }
|
||||||
else SomfyRemote::sendCommand(somfy_commands::Toggle, repeat);
|
else if(!SomfyRemote::sendCommand(somfy_commands::Toggle, repeat)) return false;
|
||||||
}
|
}
|
||||||
else if(this->isToggle() && cmd == somfy_commands::Prog) {
|
else if(this->isToggle() && cmd == somfy_commands::Prog) {
|
||||||
SomfyRemote::sendCommand(somfy_commands::Toggle, repeat, stepSize);
|
if(!SomfyRemote::sendCommand(somfy_commands::Toggle, repeat, stepSize)) return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(cmd, repeat, stepSize);
|
if(!SomfyRemote::sendCommand(cmd, repeat, stepSize)) return false;
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "Send command end");
|
ESP_LOGD(TAG, "Send command end");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
bool SomfyGroup::sendCommand(somfy_commands cmd) { return this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
bool SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
||||||
ESP_LOGI(TAG, "[Group %u] sendCommand cmd=%s repeat=%u", this->getGroupId(), translateSomfyCommand(cmd).c_str(), repeat);
|
ESP_LOGI(TAG, "[Group %u] sendCommand cmd=%s repeat=%u", this->getGroupId(), translateSomfyCommand(cmd).c_str(), repeat);
|
||||||
if(!this->canTransmit()) return;
|
|
||||||
// This sendCommand function will always be called externally. sendCommand at the remote level
|
// This sendCommand function will always be called externally. sendCommand at the remote level
|
||||||
// is expected to be called internally when the motor needs commanded.
|
// is expected to be called internally when the motor needs commanded.
|
||||||
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
||||||
SomfyRemote::sendCommand(cmd, repeat, stepSize);
|
// Leave the group and its shades alone if the radio didn't take the command.
|
||||||
|
if(!SomfyRemote::sendCommand(cmd, repeat, stepSize)) return false;
|
||||||
|
|
||||||
switch(cmd) {
|
switch(cmd) {
|
||||||
case somfy_commands::My:
|
case somfy_commands::My:
|
||||||
|
|
@ -3039,25 +3040,21 @@ void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
|
||||||
}
|
}
|
||||||
this->updateFlags();
|
this->updateFlags();
|
||||||
this->emitState();
|
this->emitState();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void SomfyShade::sendTiltCommand(somfy_commands cmd) {
|
void SomfyShade::sendTiltCommand(somfy_commands cmd) {
|
||||||
if(!this->canTransmit()) return;
|
const uint8_t repeat = this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats;
|
||||||
if(cmd == somfy_commands::Up) {
|
if(cmd == somfy_commands::Up) {
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
if(SomfyRemote::sendCommand(cmd, repeat)) this->p_tiltTarget(0.0f);
|
||||||
this->p_tiltTarget(0.0f);
|
|
||||||
}
|
}
|
||||||
else if(cmd == somfy_commands::Down) {
|
else if(cmd == somfy_commands::Down) {
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
if(SomfyRemote::sendCommand(cmd, repeat)) this->p_tiltTarget(100.0f);
|
||||||
this->p_tiltTarget(100.0f);
|
|
||||||
}
|
}
|
||||||
else if(cmd == somfy_commands::My) {
|
else if(cmd == somfy_commands::My) {
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
if(SomfyRemote::sendCommand(cmd, repeat)) this->p_tiltTarget(this->currentTiltPos);
|
||||||
this->p_tiltTarget(this->currentTiltPos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SomfyShade::moveToTiltTarget(float target) {
|
void SomfyShade::moveToTiltTarget(float target) {
|
||||||
if(!this->canTransmit()) return;
|
|
||||||
somfy_commands cmd = somfy_commands::My;
|
somfy_commands cmd = somfy_commands::My;
|
||||||
if(target < this->currentTiltPos)
|
if(target < this->currentTiltPos)
|
||||||
cmd = somfy_commands::Up;
|
cmd = somfy_commands::Up;
|
||||||
|
|
@ -3069,7 +3066,7 @@ void SomfyShade::moveToTiltTarget(float target) {
|
||||||
if(cmd != somfy_commands::My) {
|
if(cmd != somfy_commands::My) {
|
||||||
ESP_LOGI(TAG, "Moving Tilt to %.2f%% from %.2f%% using %s",
|
ESP_LOGI(TAG, "Moving Tilt to %.2f%% from %.2f%% using %s",
|
||||||
target, this->currentTiltPos, translateSomfyCommand(cmd).c_str());
|
target, this->currentTiltPos, translateSomfyCommand(cmd).c_str());
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
if(!SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats)) return;
|
||||||
}
|
}
|
||||||
// If the blind is currently moving then the command to stop it
|
// If the blind is currently moving then the command to stop it
|
||||||
// will occur on its own when the tilt target is set.
|
// will occur on its own when the tilt target is set.
|
||||||
|
|
@ -3078,7 +3075,7 @@ void SomfyShade::moveToTiltTarget(float target) {
|
||||||
}
|
}
|
||||||
if(cmd != somfy_commands::My) this->settingTiltPos = true;
|
if(cmd != somfy_commands::My) this->settingTiltPos = true;
|
||||||
}
|
}
|
||||||
void SomfyShade::moveToTarget(float pos, float tilt) {
|
bool SomfyShade::moveToTarget(float pos, float tilt) {
|
||||||
ESP_LOGI(TAG, "[Shade %u] moveToTarget(pos=%.2f, tilt=%.2f) settingPos=%d direction=%d currentTarget=%.2f currentPos=%.2f",
|
ESP_LOGI(TAG, "[Shade %u] moveToTarget(pos=%.2f, tilt=%.2f) settingPos=%d direction=%d currentTarget=%.2f currentPos=%.2f",
|
||||||
this->getShadeId(), pos, tilt, this->settingPos, this->direction, this->target, this->currentPos);
|
this->getShadeId(), pos, tilt, this->settingPos, this->direction, this->target, this->currentPos);
|
||||||
somfy_commands cmd = somfy_commands::My;
|
somfy_commands cmd = somfy_commands::My;
|
||||||
|
|
@ -3087,10 +3084,10 @@ void SomfyShade::moveToTarget(float pos, float tilt) {
|
||||||
this->p_target(pos);
|
this->p_target(pos);
|
||||||
this->p_currentPos(pos);
|
this->p_currentPos(pos);
|
||||||
this->emitState();
|
this->emitState();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
if(!this->canTransmit()) return;
|
|
||||||
if(this->tiltType == tilt_types::tiltonly) {
|
if(this->tiltType == tilt_types::tiltonly) {
|
||||||
|
// A tilt only shade is always at 100%.
|
||||||
this->p_target(100.0f);
|
this->p_target(100.0f);
|
||||||
this->p_myPos(-1.0f);
|
this->p_myPos(-1.0f);
|
||||||
this->p_currentPos(100.0f);
|
this->p_currentPos(100.0f);
|
||||||
|
|
@ -3116,7 +3113,7 @@ void SomfyShade::moveToTarget(float pos, float tilt) {
|
||||||
ESP_LOGI(TAG, "Moving to %.2f%% from %.2f%% using %s",
|
ESP_LOGI(TAG, "Moving to %.2f%% from %.2f%% using %s",
|
||||||
pos, this->currentPos, translateSomfyCommand(cmd).c_str());
|
pos, this->currentPos, translateSomfyCommand(cmd).c_str());
|
||||||
}
|
}
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::euromode ? TILT_REPEATS : this->repeats);
|
if(!SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::euromode ? TILT_REPEATS : this->repeats)) return false;
|
||||||
this->settingPos = true;
|
this->settingPos = true;
|
||||||
this->p_target(pos);
|
this->p_target(pos);
|
||||||
if(tilt >= 0) {
|
if(tilt >= 0) {
|
||||||
|
|
@ -3124,6 +3121,7 @@ void SomfyShade::moveToTarget(float pos, float tilt) {
|
||||||
this->settingTiltPos = true;
|
this->settingTiltPos = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyShade::save() {
|
bool SomfyShade::save() {
|
||||||
#ifdef USE_NVS
|
#ifdef USE_NVS
|
||||||
|
|
@ -3863,74 +3861,74 @@ somfy_commands SomfyRemote::transformCommand(somfy_commands cmd) {
|
||||||
}
|
}
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
void SomfyRemote::sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat) {
|
bool SomfyRemote::sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat) {
|
||||||
if(!this->canTransmit()) return;
|
|
||||||
uint8_t flags = (this->flags >> 4) & 0x0F;
|
uint8_t flags = (this->flags >> 4) & 0x0F;
|
||||||
if(isWindy > 0) flags |= 0x01;
|
if(isWindy > 0) flags |= 0x01;
|
||||||
if(isSunny > 0) flags |= 0x02;
|
if(isSunny > 0) flags |= 0x02;
|
||||||
if(isWindy == 0) flags &= ~0x01;
|
if(isWindy == 0) flags &= ~0x01;
|
||||||
if(isSunny == 0) flags &= ~0x02;
|
if(isSunny == 0) flags &= ~0x02;
|
||||||
|
|
||||||
// Now ship this off as an 80 bit command.
|
// Now ship this off as an 80 bit command. Built aside so lastFrame only
|
||||||
this->lastFrame.remoteAddress = this->getRemoteAddress();
|
// changes when the frame is queued.
|
||||||
this->lastFrame.repeats = repeat;
|
somfy_frame_t frame = this->lastFrame;
|
||||||
this->lastFrame.bitLength = this->bitLength;
|
frame.remoteAddress = this->getRemoteAddress();
|
||||||
this->lastFrame.rollingCode = (uint16_t)flags;
|
frame.repeats = repeat;
|
||||||
this->lastFrame.encKey = 160; // Sensor commands are always encryption code 160.
|
frame.bitLength = this->bitLength;
|
||||||
this->lastFrame.cmd = somfy_commands::Sensor;
|
frame.rollingCode = (uint16_t)flags;
|
||||||
this->lastFrame.processed = false;
|
frame.encKey = 160; // Sensor commands are always encryption code 160.
|
||||||
|
frame.cmd = somfy_commands::Sensor;
|
||||||
|
frame.processed = false;
|
||||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
|
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
|
||||||
translateSomfyCommand(this->lastFrame.cmd).c_str(),
|
translateSomfyCommand(frame.cmd).c_str(),
|
||||||
this->lastFrame.remoteAddress, this->lastFrame.rollingCode, repeat);
|
frame.remoteAddress, frame.rollingCode, repeat);
|
||||||
if(somfy.sendFrame(this->lastFrame, repeat)) somfy.processFrame(this->lastFrame, true);
|
if(!somfy.sendFrame(frame, repeat)) return false;
|
||||||
|
this->lastFrame = frame;
|
||||||
|
somfy.processFrame(this->lastFrame, true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyRemote::canTransmit() {
|
bool SomfyRemote::sendCommand(somfy_commands cmd) { return this->sendCommand(cmd, this->repeats); }
|
||||||
// GPIO remotes don't use the radio.
|
bool SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) { return this->sendCommandAfter(cmd, repeat, stepSize, 0); }
|
||||||
if(this->proto == radio_proto::GP_Relay || this->proto == radio_proto::GP_Remote) return true;
|
bool SomfyRemote::sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs) {
|
||||||
if(somfy.transceiver.canQueueTx()) return true;
|
// Build the frame aside and only take the rolling code once the frame is
|
||||||
ESP_LOGE(TAG, "Radio TX unavailable, dropping command for %d", this->getRemoteAddress());
|
// queued so a dropped command leaves the remote untouched.
|
||||||
return false;
|
somfy_frame_t frame = this->lastFrame;
|
||||||
}
|
frame.rollingCode = this->peekNextRollingCode();
|
||||||
void SomfyRemote::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
frame.remoteAddress = this->getRemoteAddress();
|
||||||
void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) { this->sendCommandAfter(cmd, repeat, stepSize, 0); }
|
frame.cmd = this->transformCommand(cmd);
|
||||||
void SomfyRemote::sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs) {
|
frame.repeats = repeat;
|
||||||
// Check before taking a rolling code so a dropped command doesn't consume one.
|
frame.bitLength = this->bitLength;
|
||||||
if(!this->canTransmit()) return;
|
frame.stepSize = stepSize;
|
||||||
this->lastFrame.rollingCode = this->getNextRollingCode();
|
frame.valid = true;
|
||||||
this->lastFrame.remoteAddress = this->getRemoteAddress();
|
|
||||||
this->lastFrame.cmd = this->transformCommand(cmd);
|
|
||||||
this->lastFrame.repeats = repeat;
|
|
||||||
this->lastFrame.bitLength = this->bitLength;
|
|
||||||
this->lastFrame.stepSize = stepSize;
|
|
||||||
this->lastFrame.valid = true;
|
|
||||||
// Match the encKey to the rolling code. These keys range from 160 to 175.
|
// Match the encKey to the rolling code. These keys range from 160 to 175.
|
||||||
this->lastFrame.encKey = 0xA0 | static_cast<uint8_t>(this->lastFrame.rollingCode & 0x000F);
|
frame.encKey = 0xA0 | static_cast<uint8_t>(frame.rollingCode & 0x000F);
|
||||||
this->lastFrame.proto = this->proto;
|
frame.proto = this->proto;
|
||||||
if(this->lastFrame.bitLength == 0) this->lastFrame.bitLength = bit_length;
|
if(frame.bitLength == 0) frame.bitLength = bit_length;
|
||||||
if(this->lastFrame.rollingCode == 0) ESP_LOGE(TAG, "Setting rcode to 0");
|
if(frame.rollingCode == 0) ESP_LOGE(TAG, "Setting rcode to 0");
|
||||||
this->p_lastRollingCode(this->lastFrame.rollingCode);
|
|
||||||
// We have to set the processed to clear this if we are sending
|
// We have to set the processed to clear this if we are sending
|
||||||
// another command.
|
// another command.
|
||||||
this->lastFrame.processed = false;
|
frame.processed = false;
|
||||||
if(this->proto == radio_proto::GP_Relay) {
|
if(this->proto == radio_proto::GP_Relay) {
|
||||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d SETTING GPIO",
|
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d SETTING GPIO",
|
||||||
translateSomfyCommand(this->lastFrame.cmd).c_str(),
|
translateSomfyCommand(frame.cmd).c_str(),
|
||||||
this->lastFrame.remoteAddress, this->lastFrame.rollingCode);
|
frame.remoteAddress, frame.rollingCode);
|
||||||
}
|
}
|
||||||
else if(this->proto == radio_proto::GP_Remote) {
|
else if(this->proto == radio_proto::GP_Remote) {
|
||||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d TRIGGER GPIO",
|
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d TRIGGER GPIO",
|
||||||
translateSomfyCommand(this->lastFrame.cmd).c_str(),
|
translateSomfyCommand(frame.cmd).c_str(),
|
||||||
this->lastFrame.remoteAddress, this->lastFrame.rollingCode);
|
frame.remoteAddress, frame.rollingCode);
|
||||||
this->triggerGPIOs(this->lastFrame);
|
this->triggerGPIOs(frame);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
|
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
|
||||||
translateSomfyCommand(this->lastFrame.cmd).c_str(),
|
translateSomfyCommand(frame.cmd).c_str(),
|
||||||
this->lastFrame.remoteAddress, this->lastFrame.rollingCode, repeat);
|
frame.remoteAddress, frame.rollingCode, repeat);
|
||||||
// Only simulate the command if it is actually going on air.
|
// Only simulate the command if it is actually going on air.
|
||||||
if(!somfy.sendFrame(this->lastFrame, repeat, delayMs)) return;
|
if(!somfy.sendFrame(frame, repeat, delayMs)) return false;
|
||||||
}
|
}
|
||||||
|
this->commitRollingCode(frame.rollingCode);
|
||||||
|
this->lastFrame = frame;
|
||||||
somfy.processFrame(this->lastFrame, true);
|
somfy.processFrame(this->lastFrame, true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyRemote::isLastCommand(somfy_commands cmd) {
|
bool SomfyRemote::isLastCommand(somfy_commands cmd) {
|
||||||
if(this->lastFrame.cmd != cmd || this->lastFrame.rollingCode != this->lastRollingCode) {
|
if(this->lastFrame.cmd != cmd || this->lastFrame.rollingCode != this->lastRollingCode) {
|
||||||
|
|
@ -3939,21 +3937,23 @@ bool SomfyRemote::isLastCommand(somfy_commands cmd) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void SomfyRemote::repeatFrame(uint8_t repeat) {
|
bool SomfyRemote::repeatFrame(uint8_t repeat) {
|
||||||
if(this->proto == radio_proto::GP_Relay)
|
if(this->proto == radio_proto::GP_Relay)
|
||||||
return;
|
return true;
|
||||||
else if(this->proto == radio_proto::GP_Remote) {
|
else if(this->proto == radio_proto::GP_Remote) {
|
||||||
this->triggerGPIOs(this->lastFrame);
|
this->triggerGPIOs(this->lastFrame);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
// The first frame is encoded with the current repeat counter and the radio
|
// The first frame is encoded with the current repeat counter and the radio
|
||||||
// task counts up from there for each repeat frame.
|
// task counts up from there for each repeat frame.
|
||||||
somfy_tx_job_t job;
|
somfy_tx_job_t job;
|
||||||
encodeTxJob(job, this->lastFrame, repeat);
|
encodeTxJob(job, this->lastFrame, repeat);
|
||||||
job.firstRepeat = this->lastFrame.repeats + 2;
|
job.firstRepeat = this->lastFrame.repeats + 2;
|
||||||
|
// Only advance the counter for frames that will go on air.
|
||||||
|
if(!somfy.transceiver.queueTx(job)) return false;
|
||||||
this->lastFrame.repeats += repeat + 1;
|
this->lastFrame.repeats += repeat + 1;
|
||||||
somfy.transceiver.queueTx(job);
|
|
||||||
//somfy.processFrame(this->lastFrame, true);
|
//somfy.processFrame(this->lastFrame, true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat, uint16_t delayMs, bool priority) {
|
bool SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat, uint16_t delayMs, bool priority) {
|
||||||
somfy_tx_job_t job;
|
somfy_tx_job_t job;
|
||||||
|
|
@ -4027,17 +4027,23 @@ bool SomfyShadeController::deleteGroup(uint8_t groupId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); }
|
bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); }
|
||||||
uint16_t SomfyRemote::getNextRollingCode() {
|
// The next rolling code is only read here. It is consumed by commitRollingCode
|
||||||
|
// once the frame using it has been queued.
|
||||||
|
uint16_t SomfyRemote::peekNextRollingCode() {
|
||||||
if(!pref.begin("ShadeCodes")) {
|
if(!pref.begin("ShadeCodes")) {
|
||||||
ESP_LOGE(TAG, "getNextRollingCode() pref.begin(ShadeCodes) FAILED");
|
ESP_LOGE(TAG, "peekNextRollingCode() pref.begin(ShadeCodes) FAILED");
|
||||||
}
|
}
|
||||||
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
|
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
|
||||||
code++;
|
pref.end();
|
||||||
|
return (uint16_t)(code + 1);
|
||||||
|
}
|
||||||
|
void SomfyRemote::commitRollingCode(uint16_t code) {
|
||||||
|
if(!pref.begin("ShadeCodes")) {
|
||||||
|
ESP_LOGE(TAG, "commitRollingCode() pref.begin(ShadeCodes) FAILED");
|
||||||
|
}
|
||||||
pref.putUShort(this->m_remotePrefId, code);
|
pref.putUShort(this->m_remotePrefId, code);
|
||||||
pref.end();
|
pref.end();
|
||||||
this->p_lastRollingCode(code);
|
this->p_lastRollingCode(code);
|
||||||
//Serial.printf("Getting Next Rolling code %d\n", this->lastRollingCode);
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
uint16_t SomfyRemote::p_lastRollingCode(uint16_t code) {
|
uint16_t SomfyRemote::p_lastRollingCode(uint16_t code) {
|
||||||
uint16_t old = this->lastRollingCode;
|
uint16_t old = this->lastRollingCode;
|
||||||
|
|
@ -5069,7 +5075,7 @@ void Transceiver::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool Transceiver::queueTx(somfy_tx_job_t &job) {
|
bool Transceiver::queueTx(somfy_tx_job_t &job) {
|
||||||
if(!txJobQueue) {
|
if(!txJobQueue || !txTaskHandle) {
|
||||||
ESP_LOGE(TAG, "Radio task not started, dropping transmission");
|
ESP_LOGE(TAG, "Radio task not started, dropping transmission");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -5090,9 +5096,6 @@ bool Transceiver::queueTx(somfy_tx_job_t &job) {
|
||||||
else xQueueSendToBack(txJobQueue, &job, 0);
|
else xQueueSendToBack(txJobQueue, &job, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Transceiver::canQueueTx(bool priority) {
|
|
||||||
return txJobQueue && txTaskHandle && !txSuspendMask && uxQueueSpacesAvailable(txJobQueue) > (priority ? 0 : TX_RESERVED_SLOTS);
|
|
||||||
}
|
|
||||||
void Transceiver::flushTx() {
|
void Transceiver::flushTx() {
|
||||||
TxQueueLock lock;
|
TxQueueLock lock;
|
||||||
++txGen;
|
++txGen;
|
||||||
|
|
|
||||||
29
src/Somfy.h
29
src/Somfy.h
|
|
@ -273,7 +273,8 @@ class SomfyRemote {
|
||||||
char *getRemotePrefId() {return m_remotePrefId;}
|
char *getRemotePrefId() {return m_remotePrefId;}
|
||||||
virtual void setRemoteAddress(uint32_t address);
|
virtual void setRemoteAddress(uint32_t address);
|
||||||
virtual uint32_t getRemoteAddress();
|
virtual uint32_t getRemoteAddress();
|
||||||
virtual uint16_t getNextRollingCode();
|
uint16_t peekNextRollingCode();
|
||||||
|
void commitRollingCode(uint16_t code);
|
||||||
virtual uint16_t setRollingCode(uint16_t code);
|
virtual uint16_t setRollingCode(uint16_t code);
|
||||||
bool hasSunSensor();
|
bool hasSunSensor();
|
||||||
bool hasLight();
|
bool hasLight();
|
||||||
|
|
@ -281,12 +282,13 @@ class SomfyRemote {
|
||||||
void setSunSensor(bool bHasSensor);
|
void setSunSensor(bool bHasSensor);
|
||||||
void setLight(bool bHasLight);
|
void setLight(bool bHasLight);
|
||||||
void setSimMy(bool bSimMy);
|
void setSimMy(bool bSimMy);
|
||||||
virtual void sendCommand(somfy_commands cmd);
|
// The send functions return false only when the radio could not take the
|
||||||
virtual void sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
// command. Nothing about the remote or shade state has changed in that case.
|
||||||
void sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs);
|
virtual bool sendCommand(somfy_commands cmd);
|
||||||
void sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat);
|
virtual bool sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
||||||
void repeatFrame(uint8_t repeat);
|
bool sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs);
|
||||||
bool canTransmit();
|
bool sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat);
|
||||||
|
bool repeatFrame(uint8_t repeat);
|
||||||
virtual uint16_t p_lastRollingCode(uint16_t code);
|
virtual uint16_t p_lastRollingCode(uint16_t code);
|
||||||
somfy_commands transformCommand(somfy_commands cmd);
|
somfy_commands transformCommand(somfy_commands cmd);
|
||||||
virtual void triggerGPIOs(somfy_frame_t &frame);
|
virtual void triggerGPIOs(somfy_frame_t &frame);
|
||||||
|
|
@ -358,11 +360,11 @@ class SomfyShade : public SomfyRemote {
|
||||||
void setTarget(float target);
|
void setTarget(float target);
|
||||||
bool isAtTarget();
|
bool isAtTarget();
|
||||||
bool isToggle();
|
bool isToggle();
|
||||||
void moveToTarget(float pos, float tilt = -1.0f);
|
bool moveToTarget(float pos, float tilt = -1.0f);
|
||||||
void moveToTiltTarget(float target);
|
void moveToTiltTarget(float target);
|
||||||
void sendTiltCommand(somfy_commands cmd);
|
void sendTiltCommand(somfy_commands cmd);
|
||||||
void sendCommand(somfy_commands cmd);
|
bool sendCommand(somfy_commands cmd);
|
||||||
void sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
bool sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
||||||
bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0);
|
bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0);
|
||||||
bool unlinkRemote(uint32_t remoteAddress);
|
bool unlinkRemote(uint32_t remoteAddress);
|
||||||
void emitState(const char *evt = "shadeState");
|
void emitState(const char *evt = "shadeState");
|
||||||
|
|
@ -370,7 +372,7 @@ class SomfyShade : public SomfyRemote {
|
||||||
void emitCommand(somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt = "shadeCommand");
|
void emitCommand(somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt = "shadeCommand");
|
||||||
void emitCommand(uint8_t num, somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt = "shadeCommand");
|
void emitCommand(uint8_t num, somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt = "shadeCommand");
|
||||||
void setMyPosition(int8_t pos, int8_t tilt = -1);
|
void setMyPosition(int8_t pos, int8_t tilt = -1);
|
||||||
void moveToMyPosition();
|
bool moveToMyPosition();
|
||||||
void processWaitingFrame();
|
void processWaitingFrame();
|
||||||
void publish();
|
void publish();
|
||||||
void unpublish();
|
void unpublish();
|
||||||
|
|
@ -437,8 +439,8 @@ class SomfyGroup : public SomfyRemote {
|
||||||
void updateFlags();
|
void updateFlags();
|
||||||
void emitState(const char *evt = "groupState");
|
void emitState(const char *evt = "groupState");
|
||||||
void emitState(uint8_t num, const char *evt = "groupState");
|
void emitState(uint8_t num, const char *evt = "groupState");
|
||||||
void sendCommand(somfy_commands cmd);
|
bool sendCommand(somfy_commands cmd);
|
||||||
void sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
bool sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
||||||
int8_t p_direction(int8_t dir);
|
int8_t p_direction(int8_t dir);
|
||||||
bool publish(const char *topic, uint8_t val, bool retain = false);
|
bool publish(const char *topic, uint8_t val, bool retain = false);
|
||||||
bool publish(const char *topic, int8_t val, bool retain = false);
|
bool publish(const char *topic, int8_t val, bool retain = false);
|
||||||
|
|
@ -544,7 +546,6 @@ class Transceiver {
|
||||||
somfy_frame_t& lastFrame();
|
somfy_frame_t& lastFrame();
|
||||||
void sendFrame(byte *frame, uint8_t sync, uint8_t bitLength = 56);
|
void sendFrame(byte *frame, uint8_t sync, uint8_t bitLength = 56);
|
||||||
bool queueTx(somfy_tx_job_t &job);
|
bool queueTx(somfy_tx_job_t &job);
|
||||||
bool canQueueTx(bool priority = false);
|
|
||||||
void flushTx();
|
void flushTx();
|
||||||
void suspendTx(uint8_t reason);
|
void suspendTx(uint8_t reason);
|
||||||
void resumeTx(uint8_t reason);
|
void resumeTx(uint8_t reason);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue