diff --git a/Somfy.cpp b/Somfy.cpp index 5c3a45b..e72b320 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -764,7 +764,9 @@ bool SomfyGroup::hasShadeId(uint8_t shadeId) { } bool SomfyShade::isAtTarget() { return this->currentPos == this->target && this->currentTiltPos == this->tiltTarget; } bool SomfyRemote::hasSunSensor() { return (this->flags & static_cast(somfy_flags_t::SunSensor)) > 0;} +bool SomfyRemote::hasLight() { return (this->flags & static_cast(somfy_flags_t::Light)) > 0; } void SomfyRemote::setSunSensor(bool bHasSensor ) { bHasSensor ? this->flags |= static_cast(somfy_flags_t::SunSensor) : this->flags &= ~(static_cast(somfy_flags_t::SunSensor)); } +void SomfyRemote::setLight(bool bHasLight ) { bHasLight ? this->flags |= static_cast(somfy_flags_t::Light) : this->flags &= ~(static_cast(somfy_flags_t::Light)); } void SomfyGroup::updateFlags() { this->flags = 0; for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { @@ -1217,16 +1219,16 @@ void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); } void SomfyShade::emitState(uint8_t num, const char *evt) { char buf[420]; if(this->tiltType != tilt_types::none) - snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"myTiltPos\":%d,\"tiltType\":%u,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d,\"sunSensor\":%s}", + snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"myTiltPos\":%d,\"tiltType\":%u,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d,\"sunSensor\":%s,\"light\":%s}", this->shadeId, static_cast(this->shadeType), this->getRemoteAddress(), this->name, this->direction, this->transformPosition(this->currentPos), this->transformPosition(this->target), this->transformPosition(this->myPos), this->transformPosition(this->myTiltPos), static_cast(this->tiltType), this->tiltDirection, this->transformPosition(this->tiltTarget), this->transformPosition(this->currentTiltPos), - this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false"); + this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false"); else - snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"tiltType\":%u,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d,\"sunSensor\":%s}", + snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"tiltType\":%u,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d,\"sunSensor\":%s,\"light\":%s}", this->shadeId, static_cast(this->shadeType), this->getRemoteAddress(), this->name, this->direction, this->transformPosition(this->currentPos), this->transformPosition(this->target), this->transformPosition(this->myPos), - static_cast(this->tiltType), this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false"); + static_cast(this->tiltType), this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false"); if(num >= 255) sockEmit.sendToClients(evt, buf); else sockEmit.sendToClient(num, evt, buf); if(mqtt.connected()) { @@ -2129,6 +2131,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) { if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("proto")) this->proto = static_cast(obj["proto"].as()); if(obj.containsKey("sunSensor")) this->setSunSensor(obj["sunSensor"]); + if(obj.containsKey("light")) this->setLight(obj["light"]); if(obj.containsKey("shadeType")) { if(obj["shadeType"].is()) { if(strncmp(obj["shadeType"].as(), "roller", 7) == 0) @@ -2189,6 +2192,7 @@ bool SomfyShade::toJSONRef(JsonObject &obj) { obj["proto"] = static_cast(this->proto); obj["flags"] = this->flags; obj["sunSensor"] = this->hasSunSensor(); + obj["hasLight"] = this->hasLight(); obj["repeats"] = this->repeats; SomfyRemote::toJSON(obj); return true; @@ -2226,6 +2230,7 @@ bool SomfyShade::toJSON(JsonObject &obj) { obj["flipPosition"] = this->flipPosition; obj["inGroup"] = this->isInGroup(); obj["sunSensor"] = this->hasSunSensor(); + obj["light"] = this->hasLight(); obj["repeats"] = this->repeats; SomfyRemote::toJSON(obj); diff --git a/Somfy.h b/Somfy.h index 21d2f2e..c435d43 100644 --- a/Somfy.h +++ b/Somfy.h @@ -142,8 +142,10 @@ enum class somfy_flags_t : byte { SunFlag = 0x01, SunSensor = 0x02, DemoMode = 0x04, + Light = 0x08, Windy = 0x10, - Sunny = 0x20 + Sunny = 0x20, + Lighted = 0x40 }; struct somfy_frame_t { bool valid = false; @@ -192,7 +194,9 @@ class SomfyRemote { virtual uint16_t getNextRollingCode(); virtual uint16_t setRollingCode(uint16_t code); bool hasSunSensor(); + bool hasLight(); void setSunSensor(bool bHasSensor); + void setLight(bool bHasLight); virtual void sendCommand(somfy_commands cmd); virtual void sendCommand(somfy_commands cmd, uint8_t repeat); void repeatFrame(uint8_t repeat); diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 6e4183c..bc9990e 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 fc51d12..054d268 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/data/icons.css b/data/icons.css index 0197c52..de5022a 100644 --- a/data/icons.css +++ b/data/icons.css @@ -714,7 +714,34 @@ i.icss-garage { background-color: rgba(71, 212, 255, 0); background-position:left bottom; } +i.icss-lightbulb-o { + width: .35em; + height: .1em; + border-radius: .1em; + margin: .7em .325em .2em; + box-shadow: 0 .13em, 0 .19em 0 -.03em, 0 .22em 0 -0.035em; +} + i.icss-lightbulb-o:before { + width: .65em; + height: .65em; + border-width: 0.068em; + border-style: solid; + border-radius: 100% 100% 100% .2em; + background-color: transparent; + left: 50%; + transform: translateX(-50%) rotate(-45deg); + bottom: .11em; + } + + i.icss-lightbulb-o:after { + width: .25em; + height: .2em; + border-radius: 100%; + box-shadow: inset -0.05em 0.05em; + left: .1em; + top: -.5em; + } i.icss-upload { width: 1em; height: .6em; @@ -932,6 +959,32 @@ i.icss-sun-o { transform: translate(-50%, -50%) rotate(45deg); box-shadow: .45em 0, .38em 0, -.45em 0, -.38em 0, 0 .45em, 0 .38em, 0 -.45em, 0 -.38em; } +div.button-light { + position:absolute; + margin-left:-30px; + cursor:pointer; +} + div.button-light[data-on=false] i.icss-lightbulb-o:before { + background-color:lightgray; + } + div.button-light[data-on=true] i.icss-lightbulb-o:before { + background-color: yellow; + } + + div.button-light > i.icss-lightbulb-c { + position: absolute; + font-size: 18px; + color: orange; + left: 6px; + top: 7px; + } + + div.button-light > i.icss-lightbulb-o { + position: absolute; + font-size: 32px; + color: gray; + } + div.button-sunflag { position: relative; @@ -942,7 +995,6 @@ div.button-sunflag { background-image: radial-gradient(ellipse at center, gainsboro 1%, #ccc 39%, silver 39%, gray 100%); } - div.button-sunflag > i.icss-sun-c { position: absolute; font-size: 18px; diff --git a/data/index.html b/data/index.html index ef7e685..37706d6 100644 --- a/data/index.html +++ b/data/index.html @@ -378,6 +378,12 @@ +
+
+ + +
+
diff --git a/data/index.js b/data/index.js index e10e12e..db83aa4 100644 --- a/data/index.js +++ b/data/index.js @@ -1983,6 +1983,7 @@ class Somfy { } divCtl += '
'; divCtl += `
`; + divCtl += `
`; divCtl += `
`; divCtl += `
`; divCtl += `
my
`; @@ -2010,6 +2011,9 @@ class Somfy { if (new Date().getTime() - this.btnDown > 2000) event.preventDefault(); else this.sendCommand(shadeId, cmd); } + else if (cmd === 'light') { + event.currentTarget.setAttribute('data-on', !makeBool(event.currentTarget.getAttribute('data-on'))); + } else if (cmd === 'sunflag') { if (makeBool(event.currentTarget.getAttribute('data-on'))) this.sendCommand(shadeId, 'flag'); @@ -2040,6 +2044,7 @@ class Somfy { }, 2000); } } + else if (cmd === 'light') return; else if (cmd === 'sunflag') return; else if (makeBool(elShade.getAttribute('data-tilt'))) { this.btnTimer = setTimeout(() => { @@ -2420,6 +2425,7 @@ class Somfy { let sel = document.getElementById('selShadeType'); let tilt = parseInt(document.getElementById('selTiltType').value, 10); let sun = true; + let light = false; let ico = document.getElementById('icoShade'); let type = parseInt(sel.value, 10); document.getElementById('somfyShade').setAttribute('data-shadetype', type); @@ -2459,6 +2465,7 @@ class Somfy { 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'); + light = true; sun = false; tilt = false; break; @@ -2476,6 +2483,7 @@ class Somfy { document.getElementById('divLiftSettings').style.display = tilt === 3 ? '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'; } onShadeBitLengthChanged(el) { document.getElementById('somfyShade').setAttribute('data-bitlength', el.value);