mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
refactor: move SomfyRemote into SomfyRemote.cpp
Sending commands, repeats and sensor frames, rolling codes and the remote flags move out of Somfy.cpp unchanged, with startsMovement(), which only sendCommandAfter uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b152f25d11
commit
50863123a1
2 changed files with 170 additions and 161 deletions
161
src/Somfy.cpp
161
src/Somfy.cpp
|
|
@ -34,21 +34,6 @@ extern GitUpdater git;
|
|||
// How long a shade's simulated movement waits for its command to go on air
|
||||
// before it starts anyway in case the event was lost.
|
||||
#define TX_PENDING_TIMEOUT 30000
|
||||
// Commands that can set a shade moving.
|
||||
static bool startsMovement(somfy_commands cmd) {
|
||||
switch(cmd) {
|
||||
case somfy_commands::My:
|
||||
case somfy_commands::Up:
|
||||
case somfy_commands::Down:
|
||||
case somfy_commands::StepUp:
|
||||
case somfy_commands::StepDown:
|
||||
case somfy_commands::Toggle:
|
||||
case somfy_commands::Favorite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SomfyShadeController::end() { this->transceiver.disableReceive(); }
|
||||
SomfyShadeController::SomfyShadeController() {
|
||||
|
|
@ -332,12 +317,6 @@ bool SomfyShade::isAtTarget() {
|
|||
else if(this->tiltType == tilt_types::none) return fabs(this->currentPos - this->target) < epsilon;
|
||||
return fabs(this->currentPos - this->target) < epsilon && fabs(this->currentTiltPos - this->tiltTarget) < epsilon;
|
||||
}
|
||||
bool SomfyRemote::simMy() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SimMy)) > 0; }
|
||||
void SomfyRemote::setSimMy(bool bSimMy) { bSimMy ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SimMy) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SimMy)); }
|
||||
bool SomfyRemote::hasSunSensor() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SunSensor)) > 0;}
|
||||
bool SomfyRemote::hasLight() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::Light)) > 0; }
|
||||
void SomfyRemote::setSunSensor(bool bHasSensor ) { bHasSensor ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SunSensor) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunSensor)); }
|
||||
void SomfyRemote::setLight(bool bHasLight ) { bHasLight ? this->flags |= static_cast<uint8_t>(somfy_flags_t::Light) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Light)); }
|
||||
|
||||
void SomfyGroup::updateFlags() {
|
||||
uint8_t oldFlags = this->flags;
|
||||
|
|
@ -1807,8 +1786,6 @@ bool SomfyShade::isToggle() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void SomfyRemote::setRemoteAddress(uint32_t address) { this->m_remoteAddress = address; this->m_rollingCodeSynced = false; snprintf(this->m_remotePrefId, sizeof(this->m_remotePrefId), "_%lu", (unsigned long)this->m_remoteAddress); }
|
||||
uint32_t SomfyRemote::getRemoteAddress() { return this->m_remoteAddress; }
|
||||
void SomfyShadeController::processFrame(somfy_frame_t &frame, bool internal) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||
if(this->shades[i].getShadeId() != 255) this->shades[i].processFrame(frame, internal);
|
||||
|
|
@ -2062,103 +2039,6 @@ SomfyGroup *SomfyShadeController::addGroup() {
|
|||
}
|
||||
return group;
|
||||
}
|
||||
somfy_commands SomfyRemote::transformCommand(somfy_commands cmd) { return this->flipCommands ? flipCommand(cmd) : cmd; }
|
||||
bool SomfyRemote::sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat) {
|
||||
uint8_t flags = (this->flags >> 4) & 0x0F;
|
||||
if(isWindy > 0) flags |= 0x01;
|
||||
if(isSunny > 0) flags |= 0x02;
|
||||
if(isWindy == 0) flags &= ~0x01;
|
||||
if(isSunny == 0) flags &= ~0x02;
|
||||
|
||||
// Now ship this off as an 80 bit command. Built aside so lastFrame only
|
||||
// changes when the frame is queued.
|
||||
somfy_frame_t frame = this->lastFrame;
|
||||
frame.remoteAddress = this->getRemoteAddress();
|
||||
frame.repeats = repeat;
|
||||
frame.bitLength = this->bitLength;
|
||||
frame.rollingCode = (uint16_t)flags;
|
||||
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",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode, repeat);
|
||||
if(!somfy.sendFrame(frame, repeat)) return false;
|
||||
this->lastFrame = frame;
|
||||
somfy.processFrame(this->lastFrame, true);
|
||||
return true;
|
||||
}
|
||||
bool SomfyRemote::sendCommand(somfy_commands cmd) { return this->sendCommand(cmd, this->repeats); }
|
||||
bool SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) { return this->sendCommandAfter(cmd, repeat, stepSize, 0).sent; }
|
||||
send_result_t SomfyRemote::sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs, bool priority) {
|
||||
// Build the frame aside and only take the rolling code once the frame is
|
||||
// queued so a dropped command leaves the remote untouched.
|
||||
somfy_frame_t frame = this->lastFrame;
|
||||
frame.rollingCode = this->peekNextRollingCode();
|
||||
frame.remoteAddress = this->getRemoteAddress();
|
||||
frame.cmd = this->transformCommand(cmd);
|
||||
frame.repeats = repeat;
|
||||
frame.bitLength = this->bitLength;
|
||||
frame.stepSize = stepSize;
|
||||
frame.valid = true;
|
||||
// Match the encKey to the rolling code. These keys range from 160 to 175.
|
||||
frame.encKey = 0xA0 | static_cast<uint8_t>(frame.rollingCode & 0x000F);
|
||||
frame.proto = this->proto;
|
||||
if(frame.bitLength == 0) frame.bitLength = radioBitLength;
|
||||
if(frame.rollingCode == 0) ESP_LOGE(TAG, "Setting rcode to 0");
|
||||
// We have to set the processed to clear this if we are sending
|
||||
// another command.
|
||||
frame.processed = false;
|
||||
send_result_t result;
|
||||
if(this->proto == radio_proto::GP_Relay) {
|
||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d SETTING GPIO",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode);
|
||||
}
|
||||
else if(this->proto == radio_proto::GP_Remote) {
|
||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d TRIGGER GPIO",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode);
|
||||
this->triggerGPIOs(frame);
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode, repeat);
|
||||
// Only simulate the command if it is actually going on air.
|
||||
result.ticket = somfy.sendFrame(frame, repeat, delayMs, priority);
|
||||
if(result.ticket == 0) return result;
|
||||
}
|
||||
result.sent = true;
|
||||
this->commitRollingCode(frame.rollingCode);
|
||||
this->lastFrame = frame;
|
||||
somfy.processFrame(this->lastFrame, true);
|
||||
// Idle shades (and group members) this sets moving wait for the frame to go
|
||||
// on air. Stops are sent ahead of everything else so they don't need to.
|
||||
if(result.ticket != 0 && !priority && startsMovement(frame.cmd)) somfy.markTxPending(frame.remoteAddress, result.ticket);
|
||||
return result;
|
||||
}
|
||||
bool SomfyRemote::isLastCommand(somfy_commands cmd) {
|
||||
if(this->lastFrame.cmd != cmd || this->lastFrame.rollingCode != this->lastRollingCode) {
|
||||
ESP_LOGD(TAG, "Not the last command %d: %d - %d", static_cast<uint8_t>(this->lastFrame.cmd), this->lastFrame.rollingCode, this->lastRollingCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool SomfyRemote::repeatFrame(uint8_t repeat) {
|
||||
if(this->proto == radio_proto::GP_Relay)
|
||||
return true;
|
||||
else if(this->proto == radio_proto::GP_Remote) {
|
||||
this->triggerGPIOs(this->lastFrame);
|
||||
return true;
|
||||
}
|
||||
somfy_tx_job_t job;
|
||||
encodeRepeatJob(job, this->lastFrame, repeat);
|
||||
if(!somfy.transceiver.submitTx(job)) return false;
|
||||
noteRepeatQueued(this->lastFrame, job);
|
||||
//somfy.processFrame(this->lastFrame, true);
|
||||
return true;
|
||||
}
|
||||
uint32_t SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat, uint16_t delayMs, bool priority) {
|
||||
somfy_tx_job_t job;
|
||||
encodeTxJob(job, frame, repeat);
|
||||
|
|
@ -2214,46 +2094,6 @@ bool SomfyShadeController::deleteGroup(uint8_t groupId) {
|
|||
}
|
||||
|
||||
bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); }
|
||||
// The next rolling code is consumed by commitRollingCode once the frame using
|
||||
// it has been queued. After that lastRollingCode holds what NVS does.
|
||||
uint16_t SomfyRemote::peekNextRollingCode() {
|
||||
return nextRollingCode(this->m_rollingCodeSynced, this->lastRollingCode, [this]() {
|
||||
Preferences pref;
|
||||
if(!pref.begin("ShadeCodes")) {
|
||||
ESP_LOGE(TAG, "peekNextRollingCode() pref.begin(ShadeCodes) FAILED");
|
||||
}
|
||||
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
|
||||
pref.end();
|
||||
return code;
|
||||
});
|
||||
}
|
||||
void SomfyRemote::commitRollingCode(uint16_t code) {
|
||||
Preferences pref;
|
||||
if(!pref.begin("ShadeCodes")) {
|
||||
ESP_LOGE(TAG, "commitRollingCode() pref.begin(ShadeCodes) FAILED");
|
||||
}
|
||||
pref.putUShort(this->m_remotePrefId, code);
|
||||
pref.end();
|
||||
this->p_lastRollingCode(code);
|
||||
this->m_rollingCodeSynced = true;
|
||||
}
|
||||
uint16_t SomfyRemote::p_lastRollingCode(uint16_t code) {
|
||||
uint16_t old = this->lastRollingCode;
|
||||
this->lastRollingCode = code;
|
||||
return old;
|
||||
}
|
||||
uint16_t SomfyRemote::setRollingCode(uint16_t code) {
|
||||
Preferences pref;
|
||||
if(this->lastRollingCode != code) {
|
||||
pref.begin("ShadeCodes");
|
||||
pref.putUShort(this->m_remotePrefId, code);
|
||||
pref.end();
|
||||
this->lastRollingCode = code;
|
||||
this->m_rollingCodeSynced = true;
|
||||
ESP_LOGD(TAG, "Setting Last Rolling code %d", this->lastRollingCode);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
void SomfyShadeController::loop() {
|
||||
this->transceiver.loop();
|
||||
|
|
@ -2268,7 +2108,6 @@ void SomfyShadeController::loop() {
|
|||
this->commit();
|
||||
}
|
||||
}
|
||||
SomfyLinkedRemote::SomfyLinkedRemote() {}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
170
src/SomfyRemote.cpp
Normal file
170
src/SomfyRemote.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#include <Preferences.h>
|
||||
#include "esp_log.h"
|
||||
#include "Somfy.h"
|
||||
#include "RollingCode.h"
|
||||
|
||||
static const char *TAG = "Somfy";
|
||||
|
||||
extern SomfyShadeController somfy;
|
||||
|
||||
// Commands that can set a shade moving.
|
||||
static bool startsMovement(somfy_commands cmd) {
|
||||
switch(cmd) {
|
||||
case somfy_commands::My:
|
||||
case somfy_commands::Up:
|
||||
case somfy_commands::Down:
|
||||
case somfy_commands::StepUp:
|
||||
case somfy_commands::StepDown:
|
||||
case somfy_commands::Toggle:
|
||||
case somfy_commands::Favorite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool SomfyRemote::simMy() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SimMy)) > 0; }
|
||||
void SomfyRemote::setSimMy(bool bSimMy) { bSimMy ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SimMy) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SimMy)); }
|
||||
bool SomfyRemote::hasSunSensor() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SunSensor)) > 0;}
|
||||
bool SomfyRemote::hasLight() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::Light)) > 0; }
|
||||
void SomfyRemote::setSunSensor(bool bHasSensor ) { bHasSensor ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SunSensor) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunSensor)); }
|
||||
void SomfyRemote::setLight(bool bHasLight ) { bHasLight ? this->flags |= static_cast<uint8_t>(somfy_flags_t::Light) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Light)); }
|
||||
void SomfyRemote::setRemoteAddress(uint32_t address) { this->m_remoteAddress = address; this->m_rollingCodeSynced = false; snprintf(this->m_remotePrefId, sizeof(this->m_remotePrefId), "_%lu", (unsigned long)this->m_remoteAddress); }
|
||||
uint32_t SomfyRemote::getRemoteAddress() { return this->m_remoteAddress; }
|
||||
somfy_commands SomfyRemote::transformCommand(somfy_commands cmd) { return this->flipCommands ? flipCommand(cmd) : cmd; }
|
||||
bool SomfyRemote::sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat) {
|
||||
uint8_t flags = (this->flags >> 4) & 0x0F;
|
||||
if(isWindy > 0) flags |= 0x01;
|
||||
if(isSunny > 0) flags |= 0x02;
|
||||
if(isWindy == 0) flags &= ~0x01;
|
||||
if(isSunny == 0) flags &= ~0x02;
|
||||
|
||||
// Now ship this off as an 80 bit command. Built aside so lastFrame only
|
||||
// changes when the frame is queued.
|
||||
somfy_frame_t frame = this->lastFrame;
|
||||
frame.remoteAddress = this->getRemoteAddress();
|
||||
frame.repeats = repeat;
|
||||
frame.bitLength = this->bitLength;
|
||||
frame.rollingCode = (uint16_t)flags;
|
||||
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",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode, repeat);
|
||||
if(!somfy.sendFrame(frame, repeat)) return false;
|
||||
this->lastFrame = frame;
|
||||
somfy.processFrame(this->lastFrame, true);
|
||||
return true;
|
||||
}
|
||||
bool SomfyRemote::sendCommand(somfy_commands cmd) { return this->sendCommand(cmd, this->repeats); }
|
||||
bool SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) { return this->sendCommandAfter(cmd, repeat, stepSize, 0).sent; }
|
||||
send_result_t SomfyRemote::sendCommandAfter(somfy_commands cmd, uint8_t repeat, uint8_t stepSize, uint16_t delayMs, bool priority) {
|
||||
// Build the frame aside and only take the rolling code once the frame is
|
||||
// queued so a dropped command leaves the remote untouched.
|
||||
somfy_frame_t frame = this->lastFrame;
|
||||
frame.rollingCode = this->peekNextRollingCode();
|
||||
frame.remoteAddress = this->getRemoteAddress();
|
||||
frame.cmd = this->transformCommand(cmd);
|
||||
frame.repeats = repeat;
|
||||
frame.bitLength = this->bitLength;
|
||||
frame.stepSize = stepSize;
|
||||
frame.valid = true;
|
||||
// Match the encKey to the rolling code. These keys range from 160 to 175.
|
||||
frame.encKey = 0xA0 | static_cast<uint8_t>(frame.rollingCode & 0x000F);
|
||||
frame.proto = this->proto;
|
||||
if(frame.bitLength == 0) frame.bitLength = radioBitLength;
|
||||
if(frame.rollingCode == 0) ESP_LOGE(TAG, "Setting rcode to 0");
|
||||
// We have to set the processed to clear this if we are sending
|
||||
// another command.
|
||||
frame.processed = false;
|
||||
send_result_t result;
|
||||
if(this->proto == radio_proto::GP_Relay) {
|
||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d SETTING GPIO",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode);
|
||||
}
|
||||
else if(this->proto == radio_proto::GP_Remote) {
|
||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d TRIGGER GPIO",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode);
|
||||
this->triggerGPIOs(frame);
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
|
||||
translateSomfyCommand(frame.cmd).c_str(),
|
||||
frame.remoteAddress, frame.rollingCode, repeat);
|
||||
// Only simulate the command if it is actually going on air.
|
||||
result.ticket = somfy.sendFrame(frame, repeat, delayMs, priority);
|
||||
if(result.ticket == 0) return result;
|
||||
}
|
||||
result.sent = true;
|
||||
this->commitRollingCode(frame.rollingCode);
|
||||
this->lastFrame = frame;
|
||||
somfy.processFrame(this->lastFrame, true);
|
||||
// Idle shades (and group members) this sets moving wait for the frame to go
|
||||
// on air. Stops are sent ahead of everything else so they don't need to.
|
||||
if(result.ticket != 0 && !priority && startsMovement(frame.cmd)) somfy.markTxPending(frame.remoteAddress, result.ticket);
|
||||
return result;
|
||||
}
|
||||
bool SomfyRemote::isLastCommand(somfy_commands cmd) {
|
||||
if(this->lastFrame.cmd != cmd || this->lastFrame.rollingCode != this->lastRollingCode) {
|
||||
ESP_LOGD(TAG, "Not the last command %d: %d - %d", static_cast<uint8_t>(this->lastFrame.cmd), this->lastFrame.rollingCode, this->lastRollingCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool SomfyRemote::repeatFrame(uint8_t repeat) {
|
||||
if(this->proto == radio_proto::GP_Relay)
|
||||
return true;
|
||||
else if(this->proto == radio_proto::GP_Remote) {
|
||||
this->triggerGPIOs(this->lastFrame);
|
||||
return true;
|
||||
}
|
||||
somfy_tx_job_t job;
|
||||
encodeRepeatJob(job, this->lastFrame, repeat);
|
||||
if(!somfy.transceiver.submitTx(job)) return false;
|
||||
noteRepeatQueued(this->lastFrame, job);
|
||||
//somfy.processFrame(this->lastFrame, true);
|
||||
return true;
|
||||
}
|
||||
// The next rolling code is consumed by commitRollingCode once the frame using
|
||||
// it has been queued. After that lastRollingCode holds what NVS does.
|
||||
uint16_t SomfyRemote::peekNextRollingCode() {
|
||||
return nextRollingCode(this->m_rollingCodeSynced, this->lastRollingCode, [this]() {
|
||||
Preferences pref;
|
||||
if(!pref.begin("ShadeCodes")) {
|
||||
ESP_LOGE(TAG, "peekNextRollingCode() pref.begin(ShadeCodes) FAILED");
|
||||
}
|
||||
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
|
||||
pref.end();
|
||||
return code;
|
||||
});
|
||||
}
|
||||
void SomfyRemote::commitRollingCode(uint16_t code) {
|
||||
Preferences pref;
|
||||
if(!pref.begin("ShadeCodes")) {
|
||||
ESP_LOGE(TAG, "commitRollingCode() pref.begin(ShadeCodes) FAILED");
|
||||
}
|
||||
pref.putUShort(this->m_remotePrefId, code);
|
||||
pref.end();
|
||||
this->p_lastRollingCode(code);
|
||||
this->m_rollingCodeSynced = true;
|
||||
}
|
||||
uint16_t SomfyRemote::p_lastRollingCode(uint16_t code) {
|
||||
uint16_t old = this->lastRollingCode;
|
||||
this->lastRollingCode = code;
|
||||
return old;
|
||||
}
|
||||
uint16_t SomfyRemote::setRollingCode(uint16_t code) {
|
||||
Preferences pref;
|
||||
if(this->lastRollingCode != code) {
|
||||
pref.begin("ShadeCodes");
|
||||
pref.putUShort(this->m_remotePrefId, code);
|
||||
pref.end();
|
||||
this->lastRollingCode = code;
|
||||
this->m_rollingCodeSynced = true;
|
||||
ESP_LOGD(TAG, "Setting Last Rolling code %d", this->lastRollingCode);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
SomfyLinkedRemote::SomfyLinkedRemote() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue