diff --git a/ConfigSettings.h b/ConfigSettings.h index 5f5db39..f5a5d0a 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -3,7 +3,7 @@ #ifndef configsettings_h #define configsettings_h #include "WResp.h" -#define FW_VERSION "v2.4.4" +#define FW_VERSION "v2.4.5" enum class conn_types_t : byte { unset = 0x00, wifi = 0x01, diff --git a/Somfy.cpp b/Somfy.cpp index df1a670..bffebd2 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -992,7 +992,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { int8_t dir = 0; switch(frame.cmd) { case somfy_commands::My: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + if(this->shadeType != shade_types::drycontact && !this->isToggle()) { digitalWrite(this->gpioUp, p_off); digitalWrite(this->gpioDown, p_off); digitalWrite(this->gpioMy, p_on); @@ -1001,7 +1001,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { } break; case somfy_commands::Up: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { + 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); @@ -1011,7 +1011,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { break; case somfy_commands::Toggle: case somfy_commands::Down: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { + if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) { digitalWrite(this->gpioMy, p_off); digitalWrite(this->gpioUp, p_off); } @@ -1020,7 +1020,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { Serial.printf("UP: false, DOWN: true, MY: false\n"); break; case somfy_commands::MyUp: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { + 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); @@ -1028,7 +1028,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { } break; case somfy_commands::MyDown: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { + 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); @@ -1036,7 +1036,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { } break; case somfy_commands::MyUpDown: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { + 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); @@ -1504,6 +1504,9 @@ void SomfyShade::publishDisco() { case shade_types::lgate: case shade_types::cgate: case shade_types::rgate: + case shade_types::lgate1: + case shade_types::cgate1: + case shade_types::rgate1: case shade_types::ldrapery: case shade_types::rdrapery: case shade_types::cdrapery: @@ -2392,7 +2395,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { break; case somfy_commands::My: if(this->shadeType == shade_types::drycontact2) return; - if(this->shadeType == shade_types::garage1) { + if(this->isToggle()) { // This is a one button device if(this->lastFrame.processed) return; this->lastFrame.processed = true; if(!this->isIdle()) this->p_target(this->currentPos); @@ -2925,7 +2928,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz } } else if(cmd == somfy_commands::My) { - if(this->shadeType == shade_types::garage1 || this->shadeType == shade_types::drycontact) + if(this->isToggle() || this->shadeType == shade_types::drycontact) SomfyRemote::sendCommand(cmd, repeat); else if(this->shadeType == shade_types::drycontact2) return; else if(this->isIdle()) { @@ -2942,7 +2945,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz if(this->bitLength != 80) SomfyRemote::sendCommand(somfy_commands::My, repeat, stepSize); else SomfyRemote::sendCommand(somfy_commands::Toggle, repeat); } - else if(this->shadeType == shade_types::garage1 && cmd == somfy_commands::Prog) { + else if(this->isToggle() && cmd == somfy_commands::Prog) { SomfyRemote::sendCommand(somfy_commands::Toggle, repeat, stepSize); } else { @@ -3024,8 +3027,8 @@ void SomfyShade::moveToTiltTarget(float target) { } void SomfyShade::moveToTarget(float pos, float tilt) { somfy_commands cmd = somfy_commands::My; - if(this->shadeType == shade_types::garage1) { - // Overload this as we cannot seek a position on a garage door. + if(this->isToggle()) { + // Overload this as we cannot seek a position on a garage door or single button device. this->p_target(pos); this->p_currentPos(pos); this->emitState(); @@ -3107,12 +3110,24 @@ bool SomfyShade::save() { } bool SomfyRoom::save() { somfy.commit(); return true; } bool SomfyGroup::save() { somfy.commit(); return true; } +bool SomfyShade::isToggle() { + switch(this->shadeType) { + case shade_types::garage1: + case shade_types::lgate1: + case shade_types::cgate1: + case shade_types::rgate1: + return true; + default: + break; + } + return false; +} 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) { + else if(this->isToggle()) { if(this->proto == radio_proto::GP_Relay && this->gpioUp == pin) return true; } else if(this->shadeType == shade_types::drycontact2) { @@ -3164,7 +3179,9 @@ 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(type == shade_types::drycontact || + ((type == shade_types::garage1 || type == shade_types::lgate1 || type == shade_types::cgate1 || type == shade_types::rgate1) + && proto == radio_proto::GP_Remote)) upPin = myPin = 255; else if(type == shade_types::drycontact2) myPin = 255; if(proto == radio_proto::GP_Relay) myPin = 255; if(somfy.transceiver.config.enabled) { diff --git a/Somfy.h b/Somfy.h index 10349d7..bc5c99b 100644 --- a/Somfy.h +++ b/Somfy.h @@ -67,7 +67,10 @@ enum class shade_types : byte { drycontact2 = 0x0A, lgate = 0x0B, cgate = 0x0C, - rgate = 0x0D + rgate = 0x0D, + lgate1 = 0x0E, + cgate1 = 0x0F, + rgate1 = 0x10 }; enum class tilt_types : byte { none = 0x00, @@ -322,6 +325,7 @@ class SomfyShade : public SomfyRemote { void setMovement(int8_t dir); void setTarget(float target); bool isAtTarget(); + bool isToggle(); void moveToTarget(float pos, float tilt = -1.0f); void moveToTiltTarget(float target); void sendTiltCommand(somfy_commands cmd); diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 253026a..8fbd450 100644 Binary files a/SomfyController.ino.esp32.bin and b/SomfyController.ino.esp32.bin differ diff --git a/SomfyController.ino.esp32s3.bin b/SomfyController.ino.esp32s3.bin index 3b82122..4e42d0c 100644 Binary files a/SomfyController.ino.esp32s3.bin and b/SomfyController.ino.esp32s3.bin differ diff --git a/SomfyController.littlefs.bin b/SomfyController.littlefs.bin index 0b7c980..a05cd1a 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/data/appversion b/data/appversion index ab6d278..26f8b8b 100644 --- a/data/appversion +++ b/data/appversion @@ -1 +1 @@ -2.4.4 \ No newline at end of file +2.4.5 \ No newline at end of file diff --git a/data/index.html b/data/index.html index 64ed89c..41be767 100644 --- a/data/index.html +++ b/data/index.html @@ -8,9 +8,9 @@ - - - + + + @@ -114,7 +114,7 @@ rel="apple-touch-startup-image"> - +
@@ -517,6 +517,10 @@ + + + +
diff --git a/data/index.js b/data/index.js index 399002d..dd38ade 100644 --- a/data/index.js +++ b/data/index.js @@ -1270,7 +1270,7 @@ var security = new Security(); class General { initialized = false; - appVersion = 'v2.4.4'; + appVersion = 'v2.4.5'; reloadApp = false; init() { if (this.initialized) return; @@ -1926,6 +1926,9 @@ class Somfy { { type: 11, name: 'Gate (left)', ico: 'icss-lgate', lift: true, fcmd: true, fpos: true }, { type: 12, name: 'Gate (center)', ico: 'icss-cgate', lift: true, fcmd: true, fpos: true }, { type: 13, name: 'Gate (right)', ico: 'icss-rgate', lift: true, fcmd: true, fpos: true }, + { type: 14, name: 'Gate (1-button left)', ico: 'icss-lgate', lift: true, fcmd: true, fpos: true }, + { type: 15, name: 'Gate (1-button center)', ico: 'icss-cgate', lift: true, fcmd: true, fpos: true }, + { type: 16, name: 'Gate (1-button right)', ico: 'icss-rgate', lift: true, fcmd: true, fpos: true }, ]; init() { if (this.initialized) return; @@ -3246,6 +3249,9 @@ class Somfy { if (obj.proto === 8 || obj.proto === 9) { switch (obj.shadeType) { case 5: // Garage 1-button + case 14: // Gate left 1-button + case 15: // Gate center 1-button + case 16: // Gate right 1-button case 10: // Two button dry contact 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.'); @@ -4140,6 +4146,9 @@ class Somfy { case 5: case 9: case 10: + case 14: + case 15: + case 16: return; } let tiltType = parseInt(shade.getAttribute('data-tilt'), 10) || 0; diff --git a/data/widgets.css b/data/widgets.css index 64bb1fe..70e2a34 100644 --- a/data/widgets.css +++ b/data/widgets.css @@ -150,6 +150,9 @@ .shadectl-buttons div.button-outline { display: inline-block; } +.shadectl-buttons[data-shadetype="14"] > .cmd-button[data-cmd="sunflag"], +.shadectl-buttons[data-shadetype="15"] > .cmd-button[data-cmd="sunflag"], +.shadectl-buttons[data-shadetype="16"] > .cmd-button[data-cmd="sunflag"], .shadectl-buttons[data-shadetype="6"] > .cmd-button[data-cmd="sunflag"], .shadectl-buttons[data-shadetype="5"] > .cmd-button[data-cmd="sunflag"], .shadectl-buttons[data-shadetype="9"] > .cmd-button[data-cmd="sunflag"], @@ -159,6 +162,15 @@ .shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="my"], .shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="up"], .shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="down"], +.shadectl-buttons[data-shadetype="14"] > .button-outline[data-cmd="my"], +.shadectl-buttons[data-shadetype="14"] > .button-outline[data-cmd="up"], +.shadectl-buttons[data-shadetype="14"] > .button-outline[data-cmd="down"], +.shadectl-buttons[data-shadetype="15"] > .button-outline[data-cmd="my"], +.shadectl-buttons[data-shadetype="15"] > .button-outline[data-cmd="up"], +.shadectl-buttons[data-shadetype="15"] > .button-outline[data-cmd="down"], +.shadectl-buttons[data-shadetype="16"] > .button-outline[data-cmd="my"], +.shadectl-buttons[data-shadetype="16"] > .button-outline[data-cmd="up"], +.shadectl-buttons[data-shadetype="16"] > .button-outline[data-cmd="down"], .shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="my"] { display: none; } @@ -174,10 +186,13 @@ .shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="down"] i { top:.3em; } -.shadectl-buttons:not([data-shadetype="5"]):not([data-shadetype="9"]) > .button-outline[data-cmd="toggle"] { +.shadectl-buttons:not([data-shadetype="5"]):not([data-shadetype="9"]):not([data-shadetype="14"]):not([data-shadetype="15"]):not([data-shadetype="16"]) > .button-outline[data-cmd="toggle"] { display: none; } .somfyShadeCtl[data-shadetype="5"] .shadectl-mypos, +.somfyShadeCtl[data-shadetype="14"] .shadectl-mypos, +.somfyShadeCtl[data-shadetype="15"] .shadectl-mypos, +.somfyShadeCtl[data-shadetype="16"] .shadectl-mypos, .somfyShadeCtl[data-shadetype="9"] .shadectl-mypos, .somfyShadeCtl[data-tilt="3"] .shadectl-mypos .my-pos, .somfyShadeCtl:not([data-shadetype="1"]) .shadectl-mypos .my-pos-tilt, @@ -239,12 +254,24 @@ #somfyShade[data-proto="8"] #divGPIOMy, #somfyShade[data-bitlength="56"] #divStepSettings, #somfyShade[data-shadetype="5"] #divStepSettings, +#somfyShade[data-shadetype="14"] #divStepSettings, +#somfyShade[data-shadetype="15"] #divStepSettings, +#somfyShade[data-shadetype="16"] #divStepSettings, #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="14"] #divGPIOUp, +#somfyShade[data-proto="9"][data-shadetype="14"] #divGPIOMy, +#somfyShade[data-proto="8"][data-shadetype="14"] #divGPIOMy, +#somfyShade[data-proto="9"][data-shadetype="15"] #divGPIOUp, +#somfyShade[data-proto="9"][data-shadetype="15"] #divGPIOMy, +#somfyShade[data-proto="8"][data-shadetype="15"] #divGPIOMy, +#somfyShade[data-proto="9"][data-shadetype="16"] #divGPIOUp, +#somfyShade[data-proto="9"][data-shadetype="16"] #divGPIOMy, +#somfyShade[data-proto="8"][data-shadetype="16"] #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 {