diff --git a/ConfigSettings.cpp b/ConfigSettings.cpp index 5c34280..674c5de 100644 --- a/ConfigSettings.cpp +++ b/ConfigSettings.cpp @@ -492,6 +492,15 @@ bool EthernetSettings::toJSON(JsonObject &obj) { obj["MDIOPin"] = this->MDIOPin; return true; } +bool EthernetSettings::usesPin(uint8_t pin) { + if((this->CLKMode == 0 || this->CLKMode == 1) && pin == 0) return true; + else if(this->CLKMode == 2 && pin == 16) return true; + else if(this->CLKMode == 3 && pin == 17) return true; + else if(this->PWRPin == pin) return true; + else if(this->MDCPin == pin) return true; + else if(this->MDIOPin == pin) return true; + return false; +} bool EthernetSettings::save() { pref.begin("ETH"); pref.clear(); diff --git a/ConfigSettings.h b/ConfigSettings.h index 76d8b94..4d6f0af 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -76,6 +76,7 @@ class EthernetSettings: BaseSettings { bool load(); bool save(); void print(); + bool usesPin(uint8_t pin); }; class IPSettings: BaseSettings { public: diff --git a/Somfy.cpp b/Somfy.cpp index 7663986..fa76369 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -806,24 +806,32 @@ void SomfyShade::setGPIOs() { int8_t dir = this->direction; if(dir == 0 && this->tiltType == tilt_types::integrated) dir = this->tiltDirection; - switch(dir) { - case -1: - digitalWrite(this->gpioDown, LOW); - digitalWrite(this->gpioUp, HIGH); - if(dir != this->gpioDir) Serial.printf("UP: true, DOWN: false\n"); - break; - case 1: - digitalWrite(this->gpioUp, LOW); - digitalWrite(this->gpioDown, HIGH); - if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: true\n"); - break; - default: - digitalWrite(this->gpioUp, LOW); - digitalWrite(this->gpioDown, LOW); - if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: false\n"); - break; + if(this->shadeType == shade_types::drycontact) { + digitalWrite(this->gpioDown, this->currentPos == 100 ? HIGH : LOW); + this->gpioDir = this->currentPos == 100 ? 1 : -1; + } + else { + switch(dir) { + case -1: + digitalWrite(this->gpioDown, LOW); + digitalWrite(this->gpioUp, HIGH); + if(dir != this->gpioDir) Serial.printf("UP: true, DOWN: false\n"); + this->gpioDir = dir; + break; + case 1: + digitalWrite(this->gpioUp, LOW); + digitalWrite(this->gpioDown, HIGH); + if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: true\n"); + this->gpioDir = dir; + break; + default: + digitalWrite(this->gpioUp, LOW); + digitalWrite(this->gpioDown, LOW); + if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: false\n"); + this->gpioDir = dir; + break; + } } - this->gpioDir = dir; } else if(this->proto == radio_proto::GP_Remote) { if(millis() > this->gpioRelease) { @@ -834,49 +842,62 @@ void SomfyShade::setGPIOs() { } } } -void SomfyRemote::triggerGPIOs(somfy_frame_t &frame) { +void SomfyRemote::triggerGPIOs(somfy_frame_t &frame) { } +void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { if(this->proto == radio_proto::GP_Remote) { int8_t dir = 0; switch(frame.cmd) { case somfy_commands::My: - digitalWrite(this->gpioUp, LOW); - digitalWrite(this->gpioDown, LOW); - digitalWrite(this->gpioMy, HIGH); - dir = 0; - if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: false, MY: true\n"); + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + digitalWrite(this->gpioUp, LOW); + digitalWrite(this->gpioDown, LOW); + digitalWrite(this->gpioMy, HIGH); + dir = 0; + if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: false, MY: true\n"); + } break; case somfy_commands::Up: - digitalWrite(this->gpioMy, LOW); - digitalWrite(this->gpioDown, LOW); - digitalWrite(this->gpioUp, HIGH); - dir = -1; - Serial.printf("UP: true, DOWN: false, MY: false\n"); + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + digitalWrite(this->gpioMy, LOW); + digitalWrite(this->gpioDown, LOW); + digitalWrite(this->gpioUp, HIGH); + dir = -1; + Serial.printf("UP: true, DOWN: false, MY: false\n"); + } break; case somfy_commands::Toggle: case somfy_commands::Down: - digitalWrite(this->gpioMy, LOW); - digitalWrite(this->gpioUp, LOW); + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + digitalWrite(this->gpioMy, LOW); + digitalWrite(this->gpioUp, LOW); + } digitalWrite(this->gpioDown, HIGH); dir = 1; Serial.printf("UP: false, DOWN: true, MY: false\n"); break; case somfy_commands::MyUp: - digitalWrite(this->gpioDown, LOW); - digitalWrite(this->gpioMy, HIGH); - digitalWrite(this->gpioUp, HIGH); - Serial.printf("UP: true, DOWN: false, MY: true\n"); + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + digitalWrite(this->gpioDown, LOW); + digitalWrite(this->gpioMy, HIGH); + digitalWrite(this->gpioUp, HIGH); + Serial.printf("UP: true, DOWN: false, MY: true\n"); + } break; case somfy_commands::MyDown: - digitalWrite(this->gpioUp, LOW); - digitalWrite(this->gpioMy, HIGH); - digitalWrite(this->gpioDown, HIGH); - Serial.printf("UP: true, DOWN: false, MY: true\n"); + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + digitalWrite(this->gpioUp, LOW); + digitalWrite(this->gpioMy, HIGH); + digitalWrite(this->gpioDown, HIGH); + Serial.printf("UP: false, DOWN: true, MY: true\n"); + } break; case somfy_commands::MyUpDown: - digitalWrite(this->gpioUp, HIGH); - digitalWrite(this->gpioMy, HIGH); - digitalWrite(this->gpioDown, HIGH); - Serial.printf("UP: true, DOWN: true, MY: true\n"); + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + digitalWrite(this->gpioUp, HIGH); + digitalWrite(this->gpioMy, HIGH); + digitalWrite(this->gpioDown, HIGH); + Serial.printf("UP: true, DOWN: true, MY: true\n"); + } break; } this->gpioRelease = millis() + (frame.repeats * 200); @@ -2492,8 +2513,50 @@ bool SomfyShade::save() { return true; } bool SomfyGroup::save() { somfy.commit(); return true; } +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->shadeType == shade_types::garage1) { + if(this->proto == radio_proto::GP_Relay && this->gpioUp == 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; +} int8_t SomfyShade::validateJSON(JsonObject &obj) { int8_t ret = 0; + shade_types type = this->shadeType; + if(obj.containsKey("shadeType")) { + if(obj["shadeType"].is()) { + if(strncmp(obj["shadeType"].as(), "roller", 7) == 0) + type = shade_types::roller; + else if(strncmp(obj["shadeType"].as(), "ldrapery", 9) == 0) + type = shade_types::ldrapery; + else if(strncmp(obj["shadeType"].as(), "rdrapery", 9) == 0) + type = shade_types::rdrapery; + else if(strncmp(obj["shadeType"].as(), "cdrapery", 9) == 0) + type = shade_types::cdrapery; + else if(strncmp(obj["shadeType"].as(), "garage1", 7) == 0) + type = shade_types::garage1; + else if(strncmp(obj["shadeType"].as(), "garage3", 7) == 0) + type = shade_types::garage3; + else if(strncmp(obj["shadeType"].as(), "blind", 5) == 0) + type = shade_types::blind; + else if(strncmp(obj["shadeType"].as(), "awning", 7) == 0) + type = shade_types::awning; + else if(strncmp(obj["shadeType"].as(), "shutter", 8) == 0) + type = shade_types::shutter; + else if(strncmp(obj["shadeType"].as(), "drycontact", 11) == 0) + type = shade_types::drycontact; + } + else { + this->shadeType = static_cast(obj["shadeType"].as()); + } + } if(obj.containsKey("proto")) { radio_proto proto = this->proto; if(proto == radio_proto::GP_Relay || proto == radio_proto::GP_Remote) { @@ -2502,63 +2565,29 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) { uint8_t upPin = obj.containsKey("gpioUp") ? obj["gpioUp"].as() : this->gpioUp; uint8_t downPin = obj.containsKey("gpioDown") ? obj["gpioDown"].as() : this->gpioDown; uint8_t myPin = obj.containsKey("gpioMy") ? obj["gpioMy"].as() : this->gpioMy; + if(type == shade_types::drycontact || (type == shade_types::garage1 && proto == radio_proto::GP_Remote)) upPin = myPin = 255; + if(proto == radio_proto::GP_Relay) myPin = 255; if(somfy.transceiver.config.enabled) { - if(somfy.transceiver.config.SCKPin == upPin || somfy.transceiver.config.SCKPin == downPin || - somfy.transceiver.config.TXPin == upPin || somfy.transceiver.config.TXPin == downPin || - somfy.transceiver.config.RXPin == upPin || somfy.transceiver.config.RXPin == downPin || - somfy.transceiver.config.MOSIPin == upPin || somfy.transceiver.config.MOSIPin == downPin || - somfy.transceiver.config.MISOPin == upPin || somfy.transceiver.config.MISOPin == downPin || - somfy.transceiver.config.CSNPin == upPin || somfy.transceiver.config.CSNPin == downPin) - ret = -10; // Pin in use with transceiver. - else if(proto == radio_proto::GP_Remote) { - if(somfy.transceiver.config.SCKPin == myPin || - somfy.transceiver.config.TXPin == myPin || - somfy.transceiver.config.RXPin == myPin || - somfy.transceiver.config.MOSIPin == myPin || - somfy.transceiver.config.MISOPin == myPin || - somfy.transceiver.config.CSNPin == myPin) - ret = -10; // Pin in use with transceiver. - } + if((upPin != 255 && somfy.transceiver.usesPin(upPin)) || + (downPin != 255 && somfy.transceiver.usesPin(downPin)) || + (myPin != 255 && somfy.transceiver.usesPin(myPin))) + ret = -10; } if(settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) { - if((settings.Ethernet.CLKMode == 0 || settings.Ethernet.CLKMode == 1) && (upPin == 0 || downPin == 0)) - ret = -11; // Pin in use with ethernet. - else if(proto == radio_proto::GP_Remote && ((settings.Ethernet.CLKMode == 0 || settings.Ethernet.CLKMode == 1) && myPin == 0)) - ret = -11; // Pin in use with ethernet. - else if((settings.Ethernet.CLKMode == 2 && (upPin == 16 || downPin == 16)) || - (settings.Ethernet.CLKMode == 3 && (upPin == 17 || downPin == 17))) - ret = -11; - else if(proto == radio_proto::GP_Remote && (settings.Ethernet.CLKMode == 2 && myPin == 16 || settings.Ethernet.CLKMode == 3 && myPin == 17)) - ret = -11; - else if(settings.Ethernet.PWRPin == upPin || settings.Ethernet.PWRPin == downPin || - settings.Ethernet.MDCPin == upPin || settings.Ethernet.MDCPin == downPin || - settings.Ethernet.MDIOPin == upPin || settings.Ethernet.MDIOPin == downPin) - ret = -11; - else if(proto == radio_proto::GP_Remote && (settings.Ethernet.PWRPin == myPin || - settings.Ethernet.MDCPin == myPin || settings.Ethernet.MDIOPin == myPin)) + if((upPin != 255 && settings.Ethernet.usesPin(upPin)) || + (downPin != 255 && somfy.transceiver.usesPin(downPin)) || + (myPin != 255 && somfy.transceiver.usesPin(myPin))) ret = -11; } if(ret == 0) { for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { SomfyShade *shade = &somfy.shades[i]; if(shade->getShadeId() == this->getShadeId() || shade->getShadeId() == 255) continue; - if(shade->proto == radio_proto::GP_Relay || shade->proto == radio_proto::GP_Remote) { - if(shade->gpioUp == upPin || shade->gpioDown == upPin || shade->gpioDown == upPin || shade->gpioDown == downPin) { - ret = -12; - break; - } - else if(proto == radio_proto::GP_Remote && (shade->gpioUp == myPin || shade->gpioDown == myPin)) { - ret = -12; - break; - } - else if(shade->proto == radio_proto::GP_Remote && (shade->gpioMy == upPin || shade->gpioMy == downPin)) { - ret = -12; - break; - } - else if(shade->proto == radio_proto::GP_Remote && proto == radio_proto::GP_Remote && (shade->gpioMy == myPin)) { - ret = -12; - break; - } + if((upPin != 255 && shade->usesPin(upPin)) || + (downPin != 255 && shade->usesPin(downPin)) || + (myPin != 255 && shade->usesPin(myPin))){ + ret = -12; + break; } } } @@ -2600,6 +2629,8 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) { this->shadeType = shade_types::awning; else if(strncmp(obj["shadeType"].as(), "shutter", 8) == 0) this->shadeType = shade_types::shutter; + else if(strncmp(obj["shadeType"].as(), "drycontact", 11) == 0) + this->shadeType = shade_types::drycontact; } else { this->shadeType = static_cast(obj["shadeType"].as()); @@ -3708,6 +3739,18 @@ bool Transceiver::fromJSON(JsonObject& obj) { } return true; } +bool Transceiver::usesPin(uint8_t pin) { + if(this->config.enabled) { + if(this->config.SCKPin == pin || + this->config.TXPin == pin || + this->config.RXPin == pin || + this->config.MOSIPin == pin || + this->config.MISOPin == pin || + this->config.CSNPin == pin) + return true; + } + return false; +} bool Transceiver::save() { this->config.save(); this->config.apply(); diff --git a/Somfy.h b/Somfy.h index 369a623..18c9b26 100644 --- a/Somfy.h +++ b/Somfy.h @@ -214,7 +214,7 @@ class SomfyRemote { void repeatFrame(uint8_t repeat); virtual uint16_t p_lastRollingCode(uint16_t code); somfy_commands transformCommand(somfy_commands cmd); - void triggerGPIOs(somfy_frame_t &frame); + virtual void triggerGPIOs(somfy_frame_t &frame); }; class SomfyLinkedRemote : public SomfyRemote { @@ -304,7 +304,8 @@ class SomfyShade : public SomfyRemote { void clear(); int8_t transformPosition(float fpos); void setGPIOs(); - + void triggerGPIOs(somfy_frame_t &frame); + bool usesPin(uint8_t pin); // State Setters int8_t p_direction(int8_t dir); int8_t p_tiltDirection(int8_t dir); @@ -450,6 +451,7 @@ class Transceiver { void endFrequencyScan(); void processFrequencyScan(bool received = false); void emitFrequencyScan(uint8_t num = 255); + bool usesPin(uint8_t pin); }; class SomfyShadeController { protected: diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 723602b..ad43bd3 100644 Binary files a/SomfyController.ino.esp32.bin and b/SomfyController.ino.esp32.bin differ diff --git a/SomfyController.littlefs.bin b/SomfyController.littlefs.bin index 057a205..b6dfd68 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/data/index.html b/data/index.html index ebfadbf..3769cf2 100644 --- a/data/index.html +++ b/data/index.html @@ -293,8 +293,8 @@ - - + + @@ -306,12 +306,12 @@
-
+
-
+
diff --git a/data/index.js b/data/index.js index ce189ea..cebdef7 100644 --- a/data/index.js +++ b/data/index.js @@ -1,7 +1,7 @@ var errors = [ - { code: -10, desc: "Pin setting in use for Transceiver" }, - { code: -11, desc: "Pin setting in use for Ethernet Adapter" }, - { code: -12, desc: "Pin setting in use on another motor" } + { code: -10, desc: "Pin setting in use for Transceiver. Output pins cannot be re-used." }, + { code: -11, desc: "Pin setting in use for Ethernet Adapter. Output pins cannot be re-used." }, + { code: -12, desc: "Pin setting in use on another motor. Output pins cannot be re-used." } ] document.oncontextmenu = (event) => { if (event.target && event.target.tagName.toLowerCase() === 'input' && (event.target.type.toLowerCase() === 'text' || event.target.type.toLowerCase() === 'password')) @@ -3112,15 +3112,25 @@ class Somfy { valid = false; } if (obj.proto === 8 || obj.proto === 9) { - if (obj.gpioUp === obj.gpioDown) { - ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down GPIO selections must be unique.'); - valid = false; - } - } - if (obj.proto === 9) { - if (obj.gpioMy === obj.gpioUp || obj.gpioMy === obj.gpioDown) { - ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down and my GPIO selections must be unique.'); - valid = false; + switch (obj.shadeType) { + case 5: // Garage 1-button + if (obj.proto !== 9 && obj.gpioUp === obj.gpioDown) { + ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down GPIO selections must be unique.'); + valid = false; + } + break; + case 9: // Dry contact. + break; + default: + if (obj.gpioUp === obj.gpioDown) { + ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down GPIO selections must be unique.'); + valid = false; + } + else if (obj.proto === 9 && (obj.gpioMy === obj.gpioUp || obj.gpioMy === obj.gpioDown)) { + ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down and my GPIO selections must be unique.'); + valid = false; + } + break; } } if (valid) { diff --git a/data/widgets.css b/data/widgets.css index 67c17f1..8f6968b 100644 --- a/data/widgets.css +++ b/data/widgets.css @@ -167,20 +167,27 @@ #somfyShade[data-shadetype="6"] #divStepSettings { display: none; } - +#somfyShade[data-proto="9"][data-shadetype="5"] #divGPIOUp, +#somfyShade[data-proto="9"][data-shadetype="5"] #divGPIOMy, +#somfyShade[data-proto="8"][data-shadetype="5"] #divGPIOMy, +#somfyShade[data-proto="9"][data-shadetype="9"] #divGPIOUp, +#somfyShade[data-proto="9"][data-shadetype="9"] #divGPIOMy, +#somfyShade[data-proto="8"][data-shadetype="9"] #divGPIOUp { + display: none; +} .group-draggable, .shade-draggable { - height:32px; - border-top:solid 2px transparent; - cursor:grab; + height: 32px; + border-top: solid 2px transparent; + cursor: grab; +} +.group-draggable.dragging *, +.group-draggable.over *, +.shade-draggable.dragging *, +.shade-draggable.over * { + pointer-events: none; +} +.group-draggable.over, +.shade-draggable.over { + border-top: solid 2px var(--shade-color, '#00bcd4'); } - .group-draggable.dragging *, - .group-draggable.over *, - .shade-draggable.dragging *, - .shade-draggable.over * { - pointer-events: none; - } - .group-draggable.over, - .shade-draggable.over { - border-top: solid 2px var(--shade-color, '#00bcd4'); - }