ESPSomfy-RTS/src/ShadeGpio.cpp
cjkas 4f8eac03ac refactor: move the shade's behaviour into ShadeBehaviour
PR B of SHADE_BEHAVIOUR_PLAN.md. What a shade does with frames, commands,
group commands and radio events moves unchanged from SomfyShade into
ShadeBehaviour, which builds natively and is pinned by test_shadebehaviour.

ShadeBehaviour reads ShadeSettings and RemoteSettings, gets the time on
every input, and holds the motion state that other files now read through
state(). It sends through MotorLink and reports through ShadeEvents
(changes, commands, save wanted, sensor flags) as things happen.
SomfyShade wraps it: its MotorLink is SomfyRemote::sendCommandAfter, which
now leaves the sender out of the echo and radio hold because the module
hears its own frame. lastFrame stays shared with the send path.

ConfigFile writes through restore() and clearTilt(), SomfyShadeOps through
setPosition(). The pinning tests label where the copies of a rule disagree
"today:", for the reconcile phase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 21:21:38 +02:00

148 lines
5.9 KiB
C++

#include "esp_log.h"
#include "Somfy.h"
static const char *TAG = "Somfy";
void SomfyShade::setGPIOs() {
if(this->proto == radio_proto::GP_Relay) {
// Determine whether the direction needs to be set.
uint8_t p_on = (this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0x00 ? HIGH : LOW;
uint8_t p_off = (this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0x00 ? LOW : HIGH;
int8_t dir = this->state().direction;
if(dir == 0 && this->tiltType == tilt_types::integrated)
dir = this->state().tiltDirection;
else if(this->tiltType == tilt_types::tiltonly)
dir = this->state().tiltDirection;
if(this->shadeType == shade_types::drycontact) {
digitalWrite(this->gpioDown, this->state().currentPos == 100 ? p_on : p_off);
this->gpioDir = this->state().currentPos == 100 ? 1 : -1;
}
else if(this->shadeType == shade_types::drycontact2) {
if(this->state().currentPos == 100) {
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioUp, p_on);
}
else {
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_on);
}
this->gpioDir = this->state().currentPos == 100 ? 1 : -1;
}
else {
switch(dir) {
case -1:
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioUp, p_on);
if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: true, DOWN: false");
this->gpioDir = dir;
break;
case 1:
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_on);
if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: false, DOWN: true");
this->gpioDir = dir;
break;
default:
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_off);
if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: false, DOWN: false");
this->gpioDir = dir;
break;
}
}
}
else if(this->proto == radio_proto::GP_Remote) {
if(millis() > this->gpioRelease) {
//uint8_t p_on = (this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0x00 ? HIGH : LOW;
uint8_t p_off = (this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0x00 ? LOW : HIGH;
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_off);
this->gpioRelease = 0;
}
}
}
void SomfyRemote::triggerGPIOs(somfy_frame_t &frame) { }
void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
if(this->proto == radio_proto::GP_Remote) {
uint8_t p_on = (this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0x00 ? HIGH : LOW;
uint8_t p_off = (this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0x00 ? LOW : HIGH;
int8_t dir = 0;
switch(frame.cmd) {
case somfy_commands::My:
if(this->shadeType != shade_types::drycontact && !this->isToggle()) {
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_on);
dir = 0;
if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: false, DOWN: false, MY: true");
}
break;
case somfy_commands::Up:
if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioMy, p_off);
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioUp, p_on);
dir = -1;
ESP_LOGD(TAG, "UP: true, DOWN: false, MY: false");
}
break;
case somfy_commands::Toggle:
case somfy_commands::Down:
if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioMy, p_off);
digitalWrite(this->gpioUp, p_off);
}
digitalWrite(this->gpioDown, p_on);
dir = 1;
ESP_LOGD(TAG, "UP: false, DOWN: true, MY: false");
break;
case somfy_commands::MyUp:
if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioUp, p_on);
ESP_LOGD(TAG, "UP: true, DOWN: false, MY: true");
}
break;
case somfy_commands::MyDown:
if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioDown, p_on);
ESP_LOGD(TAG, "UP: false, DOWN: true, MY: true");
}
break;
case somfy_commands::MyUpDown:
if(this->shadeType != shade_types::drycontact && this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioUp, p_on);
digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioDown, p_on);
ESP_LOGD(TAG, "UP: true, DOWN: true, MY: true");
}
break;
default:
break;
}
this->gpioRelease = millis() + (frame.repeats * 200);
this->gpioDir = dir;
}
}
bool SomfyShade::usesPin(uint8_t pin) {
if(this->proto != radio_proto::GP_Remote && this->proto != radio_proto::GP_Relay) return false;
if(this->gpioDown == pin) return true;
else if(this->shadeType == shade_types::drycontact)
return this->gpioDown == pin;
else if(this->isToggle()) {
if(this->proto == radio_proto::GP_Relay && this->gpioUp == pin) return true;
}
else if(this->shadeType == shade_types::drycontact2) {
if(this->proto == radio_proto::GP_Relay && (this->gpioUp == pin || this->gpioDown == pin)) return true;
}
else {
if(this->gpioUp == pin) return true;
else if(this->proto == radio_proto::GP_Remote && this->gpioMy == pin) return true;
}
return false;
}