diff --git a/ConfigSettings.h b/ConfigSettings.h index 64e909d..8d3f43e 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -3,7 +3,7 @@ #ifndef configsettings_h #define configsettings_h -#define FW_VERSION "v2.2.2b" +#define FW_VERSION "v2.2.2c" enum DeviceStatus { DS_OK = 0, DS_ERROR = 1, diff --git a/Somfy.cpp b/Somfy.cpp index 87eca83..64160ea 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -820,6 +820,17 @@ void SomfyShade::setGPIOs() { digitalWrite(this->gpioDown, this->currentPos == 100 ? HIGH : LOW); this->gpioDir = this->currentPos == 100 ? 1 : -1; } + else if(this->shadeType == shade_types::drycontact2) { + if(this->currentPos == 100) { + digitalWrite(this->gpioDown, LOW); + digitalWrite(this->gpioUp, HIGH); + } + else { + digitalWrite(this->gpioUp, LOW); + digitalWrite(this->gpioDown, HIGH); + } + this->gpioDir = this->currentPos == 100 ? 1 : -1; + } else { switch(dir) { case -1: @@ -867,7 +878,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { } break; case somfy_commands::Up: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { digitalWrite(this->gpioMy, LOW); digitalWrite(this->gpioDown, LOW); digitalWrite(this->gpioUp, HIGH); @@ -877,7 +888,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) { + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { digitalWrite(this->gpioMy, LOW); digitalWrite(this->gpioUp, LOW); } @@ -886,7 +897,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) { + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { digitalWrite(this->gpioDown, LOW); digitalWrite(this->gpioMy, HIGH); digitalWrite(this->gpioUp, HIGH); @@ -894,7 +905,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { } break; case somfy_commands::MyDown: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { digitalWrite(this->gpioUp, LOW); digitalWrite(this->gpioMy, HIGH); digitalWrite(this->gpioDown, HIGH); @@ -902,7 +913,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) { } break; case somfy_commands::MyUpDown: - if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { digitalWrite(this->gpioUp, HIGH); digitalWrite(this->gpioMy, HIGH); digitalWrite(this->gpioDown, HIGH); @@ -924,7 +935,7 @@ void SomfyShade::checkMovement() { int32_t downTime = (int32_t)this->downTime; int32_t upTime = (int32_t)this->upTime; int32_t tiltTime = (int32_t)this->tiltTime; - if(this->shadeType == shade_types::drycontact) downTime = upTime = tiltTime = 1; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) downTime = upTime = tiltTime = 1; // We are checking movement for essentially 3 types of motors. @@ -1334,6 +1345,9 @@ void SomfyShade::publishDisco() { obj["state_closing"] = this->flipPosition ? "-1" : "1"; obj["state_opening"] = this->flipPosition ? "1" : "-1"; break; + case shade_types::lgate: + case shade_types::cgate: + case shade_types::rgate: case shade_types::ldrapery: case shade_types::rdrapery: case shade_types::cdrapery: @@ -1373,6 +1387,7 @@ void SomfyShade::publishDisco() { obj["state_closing"] = this->flipPosition ? "-1" : "1"; obj["state_opening"] = this->flipPosition ? "1" : "-1"; break; + case shade_types::drycontact2: case shade_types::drycontact: break; default: @@ -1385,7 +1400,7 @@ void SomfyShade::publishDisco() { obj["state_opening"] = this->flipPosition ? "1" : "-1"; break; } - if(this->shadeType != shade_types::drycontact) { + if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::drycontact2) { if(this->tiltType != tilt_types::tiltonly) { obj["command_topic"] = "~/direction/set"; obj["position_topic"] = "~/position"; @@ -1881,7 +1896,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { switch(cmd) { case somfy_commands::Sensor: this->lastFrame.processed = true; - if(this->shadeType == shade_types::drycontact) return; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) return; { const uint8_t prevFlags = this->flags; const bool wasSunny = prevFlags & static_cast(somfy_flags_t::Sunny); @@ -1960,13 +1975,13 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { case somfy_commands::MyUpDown: case somfy_commands::UpDown: this->lastFrame.processed = true; - if(this->shadeType == shade_types::drycontact) return; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) return; this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress); break; case somfy_commands::Flag: this->lastFrame.processed = true; - if(this->shadeType == shade_types::drycontact) return; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) return; if(this->lastFrame.rollingCode & 0x8000) return; // Some sensors send bogus frames with a rollingCode >= 32768 that cause them to change the state. this->p_sunFlag(false); //this->flags &= ~(static_cast(somfy_flags_t::SunFlag)); @@ -1976,7 +1991,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { somfy.updateGroupFlags(); break; case somfy_commands::SunFlag: - if(this->shadeType == shade_types::drycontact) return; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) return; if(this->lastFrame.rollingCode & 0x8000) return; // Some sensors send bogus frames with a rollingCode >= 32768 that cause them to change the state. { const bool isWindy = this->flags & static_cast(somfy_flags_t::Windy); @@ -2009,6 +2024,13 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { this->lastFrame.processed = true; return; } + else if(this->shadeType == shade_types::drycontact2) { + if(this->lastFrame.processed) return; + this->lastFrame.processed = true; + if(this->currentPos != 0.0f) this->p_target(0); + this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress); + return; + } if(this->tiltType == tilt_types::tiltmotor || this->tiltType == tilt_types::euromode) { // Wait another half second just in case we are potentially processing a tilt. if(!internal) this->lastFrame.await = curTime + 500; @@ -2030,6 +2052,13 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { this->lastFrame.processed = true; return; } + else if(this->shadeType == shade_types::drycontact2) { + if(this->lastFrame.processed) return; + this->lastFrame.processed = true; + if(this->currentPos != 100.0f) this->p_target(100); + this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress); + return; + } if (!this->windLast || (curTime - this->windLast) >= SOMFY_NO_WIND_REMOTE_TIMEOUT) { if(this->tiltType == tilt_types::tiltmotor || this->tiltType == tilt_types::euromode) { // Wait another half seccond just in case we are potentially processing a tilt. @@ -2047,6 +2076,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->lastFrame.processed) return; this->lastFrame.processed = true; @@ -2093,7 +2123,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { case somfy_commands::StepUp: if(this->lastFrame.processed) return; this->lastFrame.processed = true; - if(this->shadeType == shade_types::drycontact) return; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) return; dir = 0; // With the step commands and integrated shades // the motor must tilt in the direction first then move @@ -2115,7 +2145,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { case somfy_commands::StepDown: if(this->lastFrame.processed) return; this->lastFrame.processed = true; - if(this->shadeType == shade_types::drycontact) return; + if(this->shadeType == shade_types::drycontact || this->shadeType == shade_types::drycontact2) return; dir = 1; // With the step commands and integrated shades // the motor must tilt in the direction first then move @@ -2474,6 +2504,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) { else if(cmd == somfy_commands::My) { if(this->shadeType == shade_types::garage1 || this->shadeType == shade_types::drycontact) SomfyRemote::sendCommand(cmd, repeat); + else if(this->shadeType == shade_types::drycontact2) return; else if(this->isIdle()) { this->moveToMyPosition(); return; @@ -2653,6 +2684,8 @@ bool SomfyShade::usesPin(uint8_t pin) { else if(this->shadeType == shade_types::garage1) { 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; @@ -2682,6 +2715,8 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) { type = shade_types::awning; else if(strncmp(obj["shadeType"].as(), "shutter", 8) == 0) type = shade_types::shutter; + else if(strncmp(obj["shadeType"].as(), "drycontact2", 12) == 0) + type = shade_types::drycontact2; else if(strncmp(obj["shadeType"].as(), "drycontact", 11) == 0) type = shade_types::drycontact; } @@ -2698,6 +2733,7 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) { 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; + else if(type == shade_types::drycontact2) myPin = 255; if(proto == radio_proto::GP_Relay) myPin = 255; if(somfy.transceiver.config.enabled) { if((upPin != 255 && somfy.transceiver.usesPin(upPin)) || @@ -2761,6 +2797,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(), "drycontact2", 12) == 0) + this->shadeType = shade_types::drycontact2; else if(strncmp(obj["shadeType"].as(), "drycontact", 11) == 0) this->shadeType = shade_types::drycontact; } diff --git a/Somfy.h b/Somfy.h index ef10b06..c52cad0 100644 --- a/Somfy.h +++ b/Somfy.h @@ -59,6 +59,10 @@ enum class shade_types : byte { rdrapery = 0x07, cdrapery = 0x08, drycontact = 0x09, + drycontact2 = 0x0A, + lgate = 0x0B, + cgate = 0x0C, + rgate = 0x0D }; enum class tilt_types : byte { none = 0x00, diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 8662c14..d091757 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 e301958..b3b69ea 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/data/icons.css b/data/icons.css index 4e00c96..d5e90bb 100644 --- a/data/icons.css +++ b/data/icons.css @@ -1248,3 +1248,227 @@ i.icss-github-o { top: .2em; transform: rotate(65deg); } +i.icss-lgate { + width: 1.1em; + height: .9em; + background-color: transparent; + border: .05em solid transparent; + border-width: 0 .1em; + margin: .1em 0 .07em; +} + + i.icss-lgate:before { + width: 1.1em; + height: 1em; + border-bottom: solid 1px; + top: -.05em; + left: -.09em; + } + + i.icss-lgate > span.icss-panel-right { + display:inline-block; + width: var(--shade-position, 0%); + height: calc(100% - .005em); + right: calc(100% - var(--shade-position, 0%)); + left: 0em; + top: 0.025em; + border-bottom: solid 0.025em gray; + border-right: solid 0.025em gray; + background-image: repeating-linear-gradient(to left, rgba(255,255,255,0) 0% 75%, var(--shade-color, currentColor) 75% 100%, rgba(255,255,255,0) 75% 0%); + background-position: 0 0, 100% 100%; + background-size: .1em 1em; + background-color: rgba(71, 212, 255, 0); + transform: rotate(180deg); + } + + i.icss-lgate > span.icss-panel-right:after { + position: absolute; + content:""; + display: inline-block; + border: solid 1px var(--shade-color, currentColor); + width: .02em; + height: .9em; + left: -.05em; + background: var(--shade-color, currentColor); + } +i.icss-lgate > span.icss-panel-left { + position: absolute; + display: inline-block; +} + i.icss-lgate > span.icss-panel-left:before { + position: absolute; + display: inline-block; + content:""; + left: -.15em; + top: -.1em; + height: 1em; + width: .1em; + border: solid 1px; + border-top-left-radius: .05em; + border-top-right-radius: .05em; + background-color: black; + } +i.icss-rgate { + width: 1.1em; + height: .9em; + background-color: transparent; + border: .05em solid transparent; + border-width: 0 .1em; + margin: .1em 0 .07em; +} + + i.icss-rgate:before { + width: 1.1em; + height: 1em; + border-bottom: solid 1px; + top: -.05em; + left: -.09em; + } + + i.icss-rgate > span.icss-panel-left { + position: absolute; + display: inline-block; + width: var(--shade-position, 0%); + height: calc(100% - .005em); + left: calc(100% - var(--shade-position, 0%)); + top: 0.025em; + border-top: solid 0.025em gray; + border-right: solid 0.025em gray; + background-image: repeating-linear-gradient(to left, rgba(255,255,255,0) 0% 75%, var(--shade-color, currentColor) 75% 100%, rgba(255,255,255,0) 75% 0%); + background-position: 0 0, 100% 100%; + background-size: .1em 1em; + background-color: rgba(71, 212, 255, 0); + margin-left:-.025em; + } + + i.icss-rgate > span.icss-panel-left:after { + position: absolute; + content: ""; + display: inline-block; + border: solid 1px var(--shade-color, currentColor); + top:-0.07em; + width: .02em; + height: .9em; + left: -.05em; + background: var(--shade-color, currentColor); + } + + i.icss-rgate > span.icss-panel-right { + position: absolute; + display: inline-block; + left:calc(100% - .05em); + } + + i.icss-rgate > span.icss-panel-right:before { + position: absolute; + display: inline-block; + content: ""; + right: -.15em; + top: -.1em; + height: 1em; + width: .1em; + border: solid 1px; + border-top-left-radius: .05em; + border-top-right-radius: .05em; + background-color: black; + } + +i.icss-cgate { + width: 1.1em; + height: .9em; + background-color: transparent; + border: .05em solid transparent; + border-width: 0 .1em; + margin: .1em 0 .07em; +} + + i.icss-cgate:before { + width: 1.1em; + height: 1em; + border-bottom: solid 1px; + top: -.05em; + left: -.09em; + } + + i.icss-cgate > span.icss-panel-left { + position: absolute; + display: inline-block; + width: calc(var(--shade-position, 0%) / 2); + height: calc(100% - .005em); + left: 0%; + top: 0.025em; + border-bottom: solid 0.025em gray; + border-right: solid 0.025em gray; + background-image: repeating-linear-gradient(to left, rgba(255,255,255,0) 0% 75%, var(--shade-color, currentColor) 75% 100%, rgba(255,255,255,0) 75% 0%); + background-position: 0 0, 100% 100%; + background-size: .1em 1em; + background-color: rgba(71, 212, 255, 0); + margin-left: -.025em; + transform: rotate(180deg); + } + + i.icss-cgate > span.icss-panel-left:after { + position: absolute; + content: ""; + display: inline-block; + border: solid 1px var(--shade-color, currentColor); + top: 0em; + width: .02em; + height: .9em; + left: -.05em; + background: var(--shade-color, currentColor); + } + i.icss-cgate > span.icss-panel-left:before { + position: absolute; + display: inline-block; + content: ""; + right: -.15em; + top: -.025em; + height: 1em; + width: .1em; + border: solid 1px; + border-bottom-left-radius: .05em; + border-bottom-right-radius: .05em; + background-color: black; + } + + + i.icss-cgate > span.icss-panel-right { + position: absolute; + display: inline-block; + width: calc(var(--shade-position, 0%) / 2); + height: calc(100% - .005em); + left: calc(100% - calc(var(--shade-position, 0%) / 2)); + top: .025em; + border-top: solid 0.025em gray; + border-right: solid 0.025em gray; + background-image: repeating-linear-gradient(to left, rgba(255,255,255,0) 0% 75%, var(--shade-color, currentColor) 75% 100%, rgba(255,255,255,0) 75% 0%); + background-position: 0 0, 100% 100%; + background-size: .1em 1em; + background-color: rgba(71, 212, 255, 0); + margin-left: -.025em; + } + i.icss-cgate > span.icss-panel-right:after { + position: absolute; + content: ""; + display: inline-block; + border: solid 1px var(--shade-color, currentColor); + top: -.072em; + width: .02em; + height: .9em; + left: -.05em; + background: var(--shade-color, currentColor); + } + i.icss-cgate > span.icss-panel-right:before { + position: absolute; + display: inline-block; + content: ""; + right: -.15em; + top: -.15em; + height: 1em; + width: .1em; + border: solid 1px; + border-top-left-radius: .05em; + border-top-right-radius: .05em; + background-color: black; + } diff --git a/data/index.html b/data/index.html index 17689c5..c945b09 100644 --- a/data/index.html +++ b/data/index.html @@ -3,11 +3,11 @@ - - - + + + - +
@@ -353,6 +353,10 @@ + + + +
diff --git a/data/index.js b/data/index.js index 186d0b7..7ed5b3c 100644 --- a/data/index.js +++ b/data/index.js @@ -169,7 +169,7 @@ Number.prototype.fmt = function (format, empty) { if (rd.length === 0 && rw.length === 0) return ''; return pfx + rw + rd + sfx; }; -var baseUrl = window.location.protocol === 'file:' ? 'http://ESPSomfyRTS' : ''; +var baseUrl = window.location.protocol === 'file:' ? 'http://192.168.1.152' : ''; //var baseUrl = ''; function makeBool(val) { if (typeof val === 'boolean') return val; @@ -1252,7 +1252,7 @@ var security = new Security(); class General { initialized = false; - appVersion = 'v2.2.2b'; + appVersion = 'v2.2.2c'; reloadApp = false; init() { if (this.initialized) return; @@ -1885,6 +1885,22 @@ var wifi = new Wifi(); class Somfy { initialized = false; frames = []; + shadeTypes = [ + { type: 0, name: 'Roller Shade', ico: 'icss-window-shade', lift: true, sun: true, fcmd: true, fpos: true }, + { type: 1, name: 'Blind', ico: 'icss-window-blind', lift: true, tilt: true, sun: true, fcmd: true, fpos: true }, + { type: 2, name: 'Drapery (left)', ico: 'icss-ldrapery', lift: true, sun: true, fcmd: true, fpos: true }, + { type: 3, name: 'Awning', ico: 'icss-awning', lift: true, sun: true, fcmd: true, fpos: true }, + { type: 4, name: 'Shutter', ico: 'icss-shutter', lift: true, sun: true, fcmd: true, fpos: true }, + { type: 5, name: 'Garage (1-button)', ico: 'icss-garage', lift: true, light: true, fpos: true }, + { type: 6, name: 'Garage (3-button)', ico: 'icss-garage', lift: true, light: true, fcmd: true, fpos: true }, + { type: 7, name: 'Drapery (right)', ico: 'icss-rdrapery', lift: true, sun: true, fcmd: true, fpos: true }, + { type: 8, name: 'Drapery (center)', ico: 'icss-cdrapery', lift: true, sun: true, fcmd: true, fpos: true }, + { type: 9, name: 'Dry Contact (1-button)', ico: 'icss-lightbulb', fpos: true }, + { type: 10, name: 'Dry Contact (2-button)', ico: 'icss-lightbulb', fcmd: true, fpos: true }, + { 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 }, + ]; init() { if (this.initialized) return; this.loadPins('inout', document.getElementById('selTransSCKPin')); @@ -2098,6 +2114,8 @@ class Somfy { } for (let i = 0; i < shades.length; i++) { let shade = shades[i]; + let st = this.shadeTypes.find(x => x.type === shade.shadeType) || { type: shade.shadeType, ico: 'icss-window-shade' }; + divCfg += `
`; divCfg += `
`; //divCfg += ``; @@ -2112,38 +2130,10 @@ class Somfy { divCtl += ` data-tiltposition="${shade.tiltPosition}" data-tiltdirection="${shade.tiltDirection}" data-tilttarget="${shade.tiltTarget}"`; } divCtl += `>
`; - divCtl += ``; + divCtl += ``; + divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.position}%;vertical-align: top;">`; + divCtl += shade.tiltType !== 0 ? `
` : '
'; divCtl += `
`; divCtl += `
`; @@ -2667,7 +2657,9 @@ class Somfy { console.log(state); let icons = document.querySelectorAll(`.somfy-shade-icon[data-shadeid="${state.shadeId}"]`); for (let i = 0; i < icons.length; i++) { - icons[i].style.setProperty('--shade-position', `${state.flipPosition ? 100 - state.position : state.position}%`); + //icons[i].style.setProperty('--shade-position', `${state.flipPosition ? 100 - state.position : state.position}%`); + icons[i].style.setProperty('--shade-position', `${state.position}%`); + } if (state.tiltType !== 0) { let tilts = document.querySelectorAll(`.icss-window-tilt[data-shadeid="${state.shadeId}"]`); @@ -2792,153 +2784,35 @@ class Somfy { onShadeTypeChanged(el) { let sel = document.getElementById('selShadeType'); let tilt = parseInt(document.getElementById('selTiltType').value, 10); - let sun = true; - let light = false; - let lift = true; - let flipCommands = true; - let flipPosition = true; let ico = document.getElementById('icoShade'); let type = parseInt(sel.value, 10); document.getElementById('somfyShade').setAttribute('data-shadetype', type); document.getElementById('divSomfyButtons').setAttribute('data-shadetype', type); - switch (type) { - case 1: - document.getElementById('divTiltSettings').style.display = ''; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (!ico.classList.contains('icss-window-blind')) ico.classList.add('icss-window-blind'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - break; - case 2: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (!ico.classList.contains('icss-ldrapery')) ico.classList.add('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - tilt = false; - break; - case 3: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (!ico.classList.contains('icss-awning')) ico.classList.add('icss-awning'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - tilt = false; - break; - case 4: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (!ico.classList.contains('icss-shutter')) ico.classList.add('icss-shutter'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - tilt = false; - break; - case 5: - flipCommands = false; - case 6: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (!ico.classList.contains('icss-garage')) ico.classList.add('icss-garage'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - light = true; - sun = false; - tilt = false; - break; - case 7: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (!ico.classList.contains('icss-rdrapery')) ico.classList.add('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - tilt = false; - break; - case 8: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (!ico.classList.contains('icss-cdrapery')) ico.classList.add('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - tilt = false; - break; - case 9: - document.getElementById('divTiltSettings').style.display = 'none'; - if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (!ico.classList.contains('icss-lightbulb')) ico.classList.add('icss-lightbulb'); - lift = false; - tilt = false; - light = false; - sun = false; - flipPosition = false; - flipCommands = false; - break; - default: - if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); - if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); - if (ico.classList.contains('icss-ldrapery')) ico.classList.remove('icss-ldrapery'); - if (ico.classList.contains('icss-rdrapery')) ico.classList.remove('icss-rdrapery'); - if (ico.classList.contains('icss-cdrapery')) ico.classList.remove('icss-cdrapery'); - if (!ico.classList.contains('icss-window-shade')) ico.classList.add('icss-window-shade'); - if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage'); - if (ico.classList.contains('icss-shutter')) ico.classList.remove('icss-shutter'); - if (ico.classList.contains('icss-lightbulb')) ico.classList.remove('icss-lightbulb'); - document.getElementById('divTiltSettings').style.display = 'none'; - tilt = false; - light = false; - break; + + let st = this.shadeTypes.find(x => x.type === type) || { type: type }; + for (let i = 0; i < this.shadeTypes.length; i++) { + let t = this.shadeTypes[i]; + if (t.type !== type) { + if (ico.classList.contains(t.ico) && t.ico !== st.ico) ico.classList.remove(t.ico); + } + else { + if (!ico.classList.contains(st.ico)) ico.classList.add(st.ico); + document.getElementById('divTiltSettings').style.display = st.tilt !== false ? '' : 'none'; + let lift = st.lift || false; + if (lift && tilt == 3) lift = false; + if (!st.tilt) tilt = 0; + document.getElementById('fldTiltTime').parentElement.style.display = tilt ? 'inline-block' : 'none'; + document.getElementById('divLiftSettings').style.display = lift ? '' : 'none'; + document.getElementById('divTiltSettings').style.display = st.tilt ? '' : 'none'; + document.querySelector('#divSomfyButtons i.icss-window-tilt').style.display = tilt ? '' : 'none'; + document.getElementById('divSunSensor').style.display = st.sun ? '' : 'none'; + document.getElementById('divLightSwitch').style.display = st.light ? '' : 'none'; + document.getElementById('divFlipPosition').style.display = st.fpos ? '' : 'none'; + document.getElementById('divFlipCommands').style.display = st.fcmd ? '' : 'none'; + if (!st.light) document.getElementById('cbHasLight').checked = false; + if (!st.sun) document.getElementById('cbHasSunsensor').checked = false; + } } - document.getElementById('fldTiltTime').parentElement.style.display = tilt ? 'inline-block' : 'none'; - if (lift && tilt == 3) lift = false; - document.getElementById('divLiftSettings').style.display = lift ? '' : 'none'; - document.querySelector('#divSomfyButtons i.icss-window-tilt').style.display = tilt ? '' : 'none'; - document.getElementById('divSunSensor').style.display = sun ? '' : 'none'; - document.getElementById('divLightSwitch').style.display = light ? '' : 'none'; - document.getElementById('divFlipPosition').style.display = flipPosition ? '' : 'none'; - document.getElementById('divFlipCommands').style.display = flipCommands ? '' : 'none'; - if (!light) document.getElementById('cbHasLight').checked = false; - if (!sun) document.getElementById('cbHasSunsensor').checked = false; } onShadeBitLengthChanged(el) { document.getElementById('somfyShade').setAttribute('data-bitlength', el.value); @@ -2997,54 +2871,15 @@ class Somfy { document.getElementById('btnLinkRemote').style.display = ''; this.onShadeTypeChanged(document.getElementById('selShadeType')); let ico = document.getElementById('icoShade'); - switch (shade.shadeType) { - case 0: - document.getElementById('divSunSensor').style.display = ''; - break; - case 1: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-window-blind'); - break; - case 2: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-ldrapery'); - document.getElementById('divSunSensor').style.display = ''; - break; - case 3: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-awning'); - document.getElementById('divSunSensor').style.display = ''; - break; - case 4: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-shutter'); - document.getElementById('divSunSensor').style.display = ''; - break; - case 5: - case 6: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-garage'); - document.getElementById('divSunSensor').style.display = 'none'; - break; - case 7: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-rdrapery'); - document.getElementById('divSunSensor').style.display = ''; - break; - case 8: - ico.classList.remove('icss-window-shade'); - ico.classList.add('icss-cdrapery'); - document.getElementById('divSunSensor').style.display = ''; - break; - - - } let tilt = ico.parentElement.querySelector('i.icss-window-tilt'); tilt.style.display = shade.tiltType !== 0 ? '' : 'none'; tilt.setAttribute('data-tiltposition', shade.tiltPosition); tilt.setAttribute('data-shadeid', shade.shadeId); - ico.style.setProperty('--shade-position', `${shade.flipPosition ? 100 - shade.position : shade.position}%`); - ico.style.setProperty('--tilt-position', `${shade.flipPosition ? 100 - shade.tiltPosition : shade.tiltPosition}%`); + //ico.style.setProperty('--shade-position', `${shade.flipPosition ? 100 - shade.position : shade.position}%`); + //ico.style.setProperty('--tilt-position', `${shade.flipPosition ? 100 - shade.tiltPosition : shade.tiltPosition}%`); + ico.style.setProperty('--shade-position', `${shade.position}%`); + ico.style.setProperty('--tilt-position', `${shade.tiltPosition}%`); + ico.setAttribute('data-shadeid', shade.shadeId); somfy.onShadeBitLengthChanged(document.getElementById('selShadeBitLength')); somfy.onShadeProtoChanged(document.getElementById('selShadeProto')); @@ -3147,6 +2982,7 @@ class Somfy { if (obj.proto === 8 || obj.proto === 9) { switch (obj.shadeType) { case 5: // Garage 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.'); valid = false; @@ -3702,23 +3538,40 @@ class Somfy { }); }); let btnPairToGroup = div.querySelector('#btnPairToGroup'); - btnPairToGroup.addEventListener('click', (evt) => { + let fnRepeatProgCommand = (err, o) => { + console.log(o); + if (this.btnTimer) { + clearTimeout(this.btnTimer); + this.btnTimer = null; + } + if (err) return; + if (mouseDown) { + if (o.cmd === 'Sensor') + somfy.sendSetSensor(o); + else if (typeof o.groupId !== 'undefined') + somfy.sendGroupRepeat(o.groupId, 'prog', null, fnRepeatProgCommand); + else + somfy.sendCommandRepeat(o.shadeId, 'prog', null, fnRepeatProgCommand); + } + } + btnPairToGroup.addEventListener('mousedown', (evt) => { + mouseDown = true; + somfy.sendGroupCommand(groupId, 'prog', null, fnRepeatProgCommand); + }); + btnPairToGroup.addEventListener('mouseup', (evt) => { + mouseDown = false; let obj = ui.fromElement(div); - putJSONSync('/groupCommand', { groupId: groupId, command: 'prog', repeat: 1 }, (err, shade) => { - if (err) ui.serviceError(err); - else { - let prompt = ui.promptMessage('Confirm Motor Response', () => { - putJSONSync('/linkToGroup', { groupId: groupId, shadeId: obj.shadeId }, (err, group) => { - console.log(group); - somfy.setLinkedShadesList(group); - this.updateGroupList(); - }); - prompt.remove(); - div.remove(); - }); - prompt.querySelector('.sub-message').innerHTML = `

Did the shade jog? If the shade jogged press the YES button and your shade will be linked to the group. If it did not press the NO button and try again.

Once the shade has jogged the shade will be added to the group and this process will be finished.

`; - } + let prompt = ui.promptMessage('Confirm Motor Response', () => { + putJSONSync('/linkToGroup', { groupId: groupId, shadeId: obj.shadeId }, (err, group) => { + console.log(group); + somfy.setLinkedShadesList(group); + this.updateGroupList(); + }); + prompt.remove(); + div.remove(); }); + prompt.querySelector('.sub-message').innerHTML = `

Did the shade jog? If the shade jogged press the YES button and your shade will be linked to the group. If it did not press the NO button and try again.

Once the shade has jogged the shade will be added to the group and this process will be finished.

`; + }); getJSONSync(`/groupOptions?groupId=${groupId}`, (err, options) => { if (err) { diff --git a/data/widgets.css b/data/widgets.css index 5af05eb..4b4fd59 100644 --- a/data/widgets.css +++ b/data/widgets.css @@ -142,9 +142,25 @@ .shadectl-buttons[data-shadetype="9"] > .button-outline[data-cmd="down"], .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="5"] > .button-outline[data-cmd="down"], +.shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="my"] { display: none; } + +.shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="up"], +.shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="down"] { + width: 3em; + border-radius: 30%; + text-align: center; + height:2.4em; +} +.shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="up"] i, +.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"] { display: none; } @@ -157,9 +173,11 @@ .somfyShadeCtl:not([data-shadetype="1"][data-tilt="3"]) .shadectl-mypos label.my-pos:after { content: "My:" } -.somfyShadeCtl[data-shadetype="1"][data-tilt="3"] .shadectl-mypos label.my-pos { - display:none; +.somfyShadeCtl[data-shadetype="1"][data-tilt="3"] .shadectl-mypos label.my-pos, +.somfyShadeCtl[data-shadetype="10"] .shadectl-mypos { + display: none; } + .somfyShadeCtl[data-shadetype="1"][data-tilt="3"] .shadectl-mypos label.my-pos-tilt:after { content:"My Tilt:"; }