Added garage door motor control #138

This commit is contained in:
Robert Strouse 2023-08-27 16:04:16 -07:00
parent ff2d150ad8
commit 74ee9f6f25
9 changed files with 246 additions and 105 deletions

126
Somfy.cpp
View file

@ -51,6 +51,7 @@ somfy_commands translateSomfyCommand(const String& string) {
else if (string.equalsIgnoreCase("StepDown")) return somfy_commands::StepDown; else if (string.equalsIgnoreCase("StepDown")) return somfy_commands::StepDown;
else if (string.equalsIgnoreCase("Flag")) return somfy_commands::Flag; else if (string.equalsIgnoreCase("Flag")) return somfy_commands::Flag;
else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor; else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor;
else if (string.equalsIgnoreCase("Toggle")) return somfy_commands::Toggle;
else if (string.startsWith("mud") || string.startsWith("MUD")) return somfy_commands::MyUpDown; else if (string.startsWith("mud") || string.startsWith("MUD")) return somfy_commands::MyUpDown;
else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown; else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown;
else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown; else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown;
@ -64,6 +65,7 @@ somfy_commands translateSomfyCommand(const String& string) {
else if (string.startsWith("m") || string.startsWith("M")) return somfy_commands::My; else if (string.startsWith("m") || string.startsWith("M")) return somfy_commands::My;
else if (string.startsWith("f") || string.startsWith("F")) return somfy_commands::Flag; else if (string.startsWith("f") || string.startsWith("F")) return somfy_commands::Flag;
else if (string.startsWith("s") || string.startsWith("S")) return somfy_commands::SunFlag; else if (string.startsWith("s") || string.startsWith("S")) return somfy_commands::SunFlag;
else if (string.startsWith("t") || string.startsWith("T")) return somfy_commands::Toggle;
else if (string.length() == 1) return static_cast<somfy_commands>(strtol(string.c_str(), nullptr, 16)); else if (string.length() == 1) return static_cast<somfy_commands>(strtol(string.c_str(), nullptr, 16));
else return somfy_commands::My; else return somfy_commands::My;
} }
@ -95,6 +97,8 @@ String translateSomfyCommand(const somfy_commands cmd) {
return "Step Down"; return "Step Down";
case somfy_commands::Sensor: case somfy_commands::Sensor:
return "Sensor"; return "Sensor";
case somfy_commands::Toggle:
return "Toggle";
default: default:
return "Unknown(" + String((uint8_t)cmd) + ")"; return "Unknown(" + String((uint8_t)cmd) + ")";
} }
@ -119,57 +123,26 @@ void somfy_frame_t::decodeFrame(byte* frame) {
this->checksum = decoded[1] & 0b1111; this->checksum = decoded[1] & 0b1111;
this->encKey = decoded[0]; this->encKey = decoded[0];
// Pull in the 80-bit commands. The upper nibble will be 0 even on 80 bit packets. // Lets first determine the protocol.
this->cmd = (somfy_commands)((decoded[1] >> 4)); this->cmd = (somfy_commands)((decoded[1] >> 4));
// Pull in the data for an 80-bit step command.
if(this->cmd == somfy_commands::StepDown)
this->cmd = (somfy_commands)((decoded[1] >> 4) | ((decoded[8] & 0x08) << 4));
if(this->cmd == somfy_commands::RTWProto) { if(this->cmd == somfy_commands::RTWProto) {
this->proto = this->encKey > 142 ? radio_proto::RTV : radio_proto::RTW; if(this->encKey >= 160) {
this->proto = radio_proto::RTS;
switch(this->encKey) { if(this->encKey == 164) this->cmd = somfy_commands::Toggle;
case 149: }
case 133: else if(this->encKey > 148) {
this->cmd = somfy_commands::My; this->proto = radio_proto::RTV;
break; this->cmd = (somfy_commands)(this->encKey - 148);
case 150: }
case 134: else if(this->encKey > 133) {
this->cmd = somfy_commands::Up; this->proto = radio_proto::RTW;
break; this->cmd = (somfy_commands)(this->encKey - 133);
case 151:
case 135:
this->cmd = somfy_commands::MyUp;
break;
case 152:
case 136:
this->cmd = somfy_commands::Down;
break;
case 153:
case 137:
this->cmd = somfy_commands::MyDown;
break;
case 154:
case 138:
this->cmd = somfy_commands::UpDown;
break;
case 155:
case 139:
this->cmd = somfy_commands::MyUpDown;
break;
case 156:
case 140:
this->cmd = somfy_commands::Prog;
break;
case 157:
case 141:
this->cmd = somfy_commands::SunFlag;
break;
case 158:
case 142:
this->cmd = somfy_commands::Flag;
break;
} }
} }
// Pull in the data for an 80-bit step command.
if(this->cmd == somfy_commands::StepDown) this->cmd = (somfy_commands)((decoded[1] >> 4) | ((decoded[8] & 0x08) << 4));
this->rollingCode = decoded[3] + (decoded[2] << 8); this->rollingCode = decoded[3] + (decoded[2] << 8);
this->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16)); this->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16));
this->valid = this->checksum == checksum && this->remoteAddress > 0 && this->remoteAddress < 16777215; this->valid = this->checksum == checksum && this->remoteAddress > 0 && this->remoteAddress < 16777215;
@ -191,13 +164,13 @@ void somfy_frame_t::decodeFrame(byte* frame) {
case somfy_commands::SunFlag: case somfy_commands::SunFlag:
case somfy_commands::Sensor: case somfy_commands::Sensor:
break; break;
case somfy_commands::UnknownC:
case somfy_commands::UnknownD: case somfy_commands::UnknownD:
case somfy_commands::RTWProto: case somfy_commands::RTWProto:
this->valid = false; this->valid = false;
break; break;
case somfy_commands::StepUp: case somfy_commands::StepUp:
case somfy_commands::StepDown: case somfy_commands::StepDown:
case somfy_commands::Toggle:
// These must be 80 bit commands // These must be 80 bit commands
break; break;
default: default:
@ -363,6 +336,8 @@ void somfy_frame_t::encodeFrame(byte *frame) {
frame[8] = 48; frame[8] = 48;
frame[9] = 30; frame[9] = 30;
break; break;
case somfy_commands::Toggle:
frame[0] = 164;
case somfy_commands::Prog: case somfy_commands::Prog:
frame[7] = 196; frame[7] = 196;
frame[8] = 0; frame[8] = 0;
@ -834,7 +809,7 @@ void SomfyShade::checkMovement() {
else if(this->direction != 0) this->tiltDirection = 0; else if(this->direction != 0) this->tiltDirection = 0;
uint8_t currPos = floor(this->currentPos); uint8_t currPos = floor(this->currentPos);
uint8_t currTiltPos = floor(this->currentTiltPos); uint8_t currTiltPos = floor(this->currentTiltPos);
if(this->direction != 0) this->lastMovement = this->direction;
if (sunFlag) { if (sunFlag) {
if (isSunny && !isWindy) { // It is sunny and there is no wind so we should be extended if (isSunny && !isWindy) { // It is sunny and there is no wind so we should be extended
if (this->noWindDone if (this->noWindDone
@ -1256,22 +1231,23 @@ void SomfyShade::emitState(uint8_t num, const char *evt) {
else sockEmit.sendToClient(num, evt, buf); else sockEmit.sendToClient(num, evt, buf);
if(mqtt.connected()) { if(mqtt.connected()) {
char topic[32]; char topic[32];
snprintf(topic, sizeof(topic), "shades/%u/shadeType", this->shadeId); //snprintf(topic, sizeof(topic), "shades/%u/shadeType", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(this->shadeType)); //mqtt.publish(topic, static_cast<uint8_t>(this->shadeType));
//snprintf(topic, sizeof(topic), "shades/%u/remoteAddress", this->shadeId);
//mqtt.publish(topic, this->getRemoteAddress());
//snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId);
//mqtt.publish(topic, static_cast<uint8_t>(this->tiltType));
//snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId);
//mqtt.publish(topic, this->lastRollingCode);
snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId);
mqtt.publish(topic, this->transformPosition(this->currentPos)); mqtt.publish(topic, this->transformPosition(this->currentPos));
snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId);
mqtt.publish(topic, this->direction); mqtt.publish(topic, this->direction);
snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId);
mqtt.publish(topic, this->transformPosition(this->target)); mqtt.publish(topic, this->transformPosition(this->target));
snprintf(topic, sizeof(topic), "shades/%u/remoteAddress", this->shadeId);
mqtt.publish(topic, this->getRemoteAddress());
snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId);
mqtt.publish(topic, this->lastRollingCode);
snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId);
mqtt.publish(topic, this->transformPosition(this->myPos)); mqtt.publish(topic, this->transformPosition(this->myPos));
snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(this->tiltType));
snprintf(topic, sizeof(topic), "shades/%u/sunSensor", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/sunSensor", this->shadeId);
mqtt.publish(topic, this->hasSunSensor()); mqtt.publish(topic, this->hasSunSensor());
@ -1283,14 +1259,15 @@ void SomfyShade::emitState(uint8_t num, const char *evt) {
snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId);
mqtt.publish(topic, this->transformPosition(this->tiltTarget)); mqtt.publish(topic, this->transformPosition(this->tiltTarget));
} }
const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag));
const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny));
const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy)); const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy));
if(this->hasSunSensor()) {
snprintf(topic, sizeof(topic), "shades/%u/sunFlag", this->shadeId); const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag));
mqtt.publish(topic, sunFlag); const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny));
snprintf(topic, sizeof(topic), "shades/%u/sunny", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/sunFlag", this->shadeId);
mqtt.publish(topic, isSunny); mqtt.publish(topic, sunFlag);
snprintf(topic, sizeof(topic), "shades/%u/sunny", this->shadeId);
mqtt.publish(topic, isSunny);
}
snprintf(topic, sizeof(topic), "shades/%u/windy", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/windy", this->shadeId);
mqtt.publish(topic, isWindy); mqtt.publish(topic, isWindy);
} }
@ -1662,6 +1639,14 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
} }
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress); this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
break; break;
case somfy_commands::Toggle:
if(!this->isIdle()) {
this->target = this->currentPos;
}
else if(this->currentPos == 100.0f) this->target = 0;
else if(this->currentPos == 0.0f) this->target = 100;
else this->target = this->lastMovement == -1 ? 100 : 0;
break;
default: default:
dir = 0; dir = 0;
break; break;
@ -1980,6 +1965,9 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
this->tiltTarget = this->currentTiltPos; this->tiltTarget = this->currentTiltPos;
} }
} }
else if(this->shadeType == shade_types::garage1 && cmd == somfy_commands::My) {
SomfyRemote::sendCommand(somfy_commands::Toggle, repeat);
}
else { else {
SomfyRemote::sendCommand(cmd, repeat); SomfyRemote::sendCommand(cmd, repeat);
} }
@ -2054,6 +2042,12 @@ void SomfyShade::moveToTiltTarget(float target) {
} }
void SomfyShade::moveToTarget(float pos, float tilt) { void SomfyShade::moveToTarget(float pos, float tilt) {
somfy_commands cmd = somfy_commands::My; somfy_commands cmd = somfy_commands::My;
if(this->shadeType == shade_types::garage1) {
// Overload this as we cannot seek a position on a garage door.
this->target = this->currentPos = pos;
this->emitState();
return;
}
if(this->tiltType == tilt_types::tiltonly) { if(this->tiltType == tilt_types::tiltonly) {
this->currentPos = this->target = 100.0f; this->currentPos = this->target = 100.0f;
pos = 100; pos = 100;
@ -2571,6 +2565,10 @@ void SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat) {
frm[7] = 132; frm[7] = 132;
frm[9] = 63; frm[9] = 63;
break; break;
case somfy_commands::Toggle:
frm[7] = 136;
frm[9] = 34;
break;
default: default:
frm[9] = 46; frm[9] = 46;
break; break;

10
Somfy.h
View file

@ -39,12 +39,12 @@ enum class somfy_commands : byte {
SunFlag = 0x9, SunFlag = 0x9,
Flag = 0xA, Flag = 0xA,
StepDown = 0xB, StepDown = 0xB,
UnknownC = 0xC, Toggle = 0xC,
UnknownD = 0xD, UnknownD = 0xD,
Sensor = 0xE, Sensor = 0xE,
RTWProto = 0xF, // RTW Protocol RTWProto = 0xF, // RTW Protocol
// Command extensions for 80 bit frames // Command extensions for 80 bit frames
StepUp = 0x8B StepUp = 0x8B,
}; };
enum class group_types : byte { enum class group_types : byte {
channel = 0x00 channel = 0x00
@ -54,7 +54,10 @@ enum class shade_types : byte {
blind = 0x01, blind = 0x01,
drapery = 0x02, drapery = 0x02,
awning = 0x03, awning = 0x03,
shutter = 0x04 shutter = 0x04,
garage1 = 0x05,
garage3 = 0x06
}; };
enum class tilt_types : byte { enum class tilt_types : byte {
none = 0x00, none = 0x00,
@ -228,6 +231,7 @@ class SomfyShade : public SomfyRemote {
float currentPos = 0.0f; float currentPos = 0.0f;
float currentTiltPos = 0.0f; float currentTiltPos = 0.0f;
//uint16_t movement = 0; //uint16_t movement = 0;
int8_t lastMovement = 0;
int8_t direction = 0; // 0 = stopped, 1=down, -1=up. int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise
float target = 0.0f; float target = 0.0f;

Binary file not shown.

Binary file not shown.

View file

@ -490,6 +490,40 @@ i.icss-somfy-up {
left: 50%; left: 50%;
transform: translate(-50%,-30%) rotate(-45deg); transform: translate(-50%,-30%) rotate(-45deg);
} }
i.icss-somfy-toggle {
width: .15em;
height: .55em;
border-radius: .1em;
margin: .2em .4em .25em .45em;
}
i.icss-somfy-toggle:before {
width: .45em;
height: .5em;
border-radius: .3em .12em 0 .2em;
border: .1em solid transparent;
border-width: 0 0 .35em 0;
background-color: currentColor;
box-shadow: -.1em 0, -.3em -.35em;
transform: rotate(-45deg);
clip: rect(-.5em .5em .15em -.5em);
left: .15em;
top: .35em;
}
i.icss-somfy-toggle:after {
width: .52em;
height: .52em;
border: .065em solid currentColor;
border-bottom-color: transparent;
border-radius: 50%;
transform: translateX(-50%);
top: -.22em;
left: 50%;
}
i.icss-home { i.icss-home {
width: .8em; width: .8em;
height: .45em; height: .45em;
@ -652,6 +686,34 @@ i.icss-window-tilt {
font-weight:bold; font-weight:bold;
font-size:.3em; font-size:.3em;
} }
i.icss-garage {
width: 1.1em;
height: .7em;
background-color: transparent;
box-shadow: inset 0 0.01em 0 0.04em, inset 0.01em 0px 0em, inset 0.01em 0.01em 1px 0em;
margin: .4em 0 0;
}
i.icss-garage:after {
border-width: 0 .8em .4em;
border-style: solid;
border-color: currentColor transparent;
transform: translateX(-50%);
top: -0.4em;
left: 50%;
clip-path: polygon(50% 0, 99% 50%, 50% 250%, 0 50%);
}
i.icss-garage:before {
width: calc(100% - .08em);
height: calc(var(--shade-position, 0%) - 0em);
left: 0.05em;
border-bottom: outset 0.025em gray;
background-image: repeating-linear-gradient(var(--shade-color, currentColor) 0% 50%, rgba(71, 212, 255, 0) 0% 75%);
background-position: 0 0, 100% 100%;
background-size: 0.01em 0.2em;
background-color: rgba(71, 212, 255, 0);
}
i.icss-upload { i.icss-upload {
width: 1em; width: 1em;

View file

@ -313,6 +313,8 @@
<option value="1">Blind</option> <option value="1">Blind</option>
<option value="2">Drapery</option> <option value="2">Drapery</option>
<option value="3">Awning</option> <option value="3">Awning</option>
<option value="5">Garage (1-button)</option>
<option value="6">Garage (3-button)</option>
</select> </select>
<label for="selShadeType">Type</label> <label for="selShadeType">Type</label>
</div> </div>
@ -322,11 +324,12 @@
</div> </div>
</div> </div>
</div> </div>
<div id="divSomfyButtons" style="float:right;margin-top:10px;position:relative"> <div id="divSomfyButtons" class="shadectl-buttons" style="float:right;margin-top:10px;position:relative">
<div style="display:inline-block;margin-right:7px;position:relative;font-size:48px;"><i id="icoShade" class="somfy-shade-icon icss-window-shade" data-shadeid="0" style="--shade-position:0%;vertical-align:middle;"></i><i class="icss-window-tilt" data-tiltposition="0" style="display:none;"></i></div> <div style="display:inline-block;margin-right:7px;position:relative;font-size:48px;"><i id="icoShade" class="somfy-shade-icon icss-window-shade" data-shadeid="0" style="--shade-position:0%;vertical-align:middle;"></i><i class="icss-window-tilt" data-tiltposition="0" style="display:none;"></i></div>
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'up');"><i class="icss-somfy-up"></i></div> <div class="button-outline" data-cmd="up" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'up');"><i class="icss-somfy-up"></i></div>
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'my');" style="font-size: 2em; padding: 10px;"><span>my</span></div> <div class="button-outline" data-cmd="my" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'my');" style="font-size: 2em; padding: 10px;"><span>my</span></div>
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'down');"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div> <div class="button-outline" data-cmd="down" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'down');"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>
<div class="button-outline toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="toggle" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'toggle');"><i class="icss-somfy-toggle" style="margin-top:-4px;"></i></div>
</div> </div>
</div> </div>
<div class="field-group" style="padding:0px;"> <div class="field-group" style="padding:0px;">
@ -358,7 +361,7 @@
<label for="fldTiltTime">Tilt Time (ms)</label> <label for="fldTiltTime">Tilt Time (ms)</label>
</div> </div>
</div> </div>
<div id="divStepSettings" style="display:none;"> <div id="divStepSettings">
<div class="field-group"> <div class="field-group">
<input id="slidStepSize" name="stepSize" type="range" min="1" max="1000" step="1" data-bind="stepSize" data-datatype="int" style="width:100%;" oninput="somfy.stepSizeChanged(this);" /> <input id="slidStepSize" name="stepSize" type="range" min="1" max="1000" step="1" data-bind="stepSize" data-datatype="int" style="width:100%;" oninput="somfy.stepSizeChanged(this);" />
<label for="slidStepSize" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;"> <label for="slidStepSize" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;">
@ -369,7 +372,7 @@
</label> </label>
</div> </div>
</div> </div>
<div style="margin-top:-10px;"> <div id="divSunSensor" style="margin-top:-10px;">
<div class="field-group"> <div class="field-group">
<input id="cbHasSunsensor" name="hasSunSensor" data-bind="sunSensor" type="checkbox" style="" /> <input id="cbHasSunsensor" name="hasSunSensor" data-bind="sunSensor" type="checkbox" style="" />
<label for="cbHasSunSensor" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Has Sun Sensor</label> <label for="cbHasSunSensor" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Has Sun Sensor</label>

View file

@ -149,7 +149,7 @@ Number.prototype.fmt = function (format, empty) {
if (rd.length === 0 && rw.length === 0) return ''; if (rd.length === 0 && rw.length === 0) return '';
return pfx + rw + rd + sfx; return pfx + rw + rd + sfx;
}; };
var baseUrl = window.location.protocol === 'file:' ? 'http://ESPSomfyRTS' : ''; var baseUrl = window.location.protocol === 'file:' ? 'http://ESPSomfyRTS.local' : '';
//var baseUrl = ''; //var baseUrl = '';
function makeBool(val) { function makeBool(val) {
if (typeof val === 'boolean') return val; if (typeof val === 'boolean') return val;
@ -1961,6 +1961,10 @@ class Somfy {
case 4: case 4:
divCtl += ' icss-shutter'; divCtl += ' icss-shutter';
break; break;
case 5:
case 6:
divCtl += ' icss-garage';
break;
default: default:
divCtl += ' icss-window-shade'; divCtl += ' icss-window-shade';
break; break;
@ -1973,18 +1977,19 @@ class Somfy {
divCtl += `<span class="shadectl-name">${shade.name}</span>`; divCtl += `<span class="shadectl-name">${shade.name}</span>`;
if (shade.tiltType === 3) if (shade.tiltType === 3)
divCtl += `<span class="shadectl-mypos"><label>My Tilt: </label><span id="spanMyTiltPos">${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}</span>` divCtl += `<span class="shadectl-mypos"><label>My Tilt: </label><span id="spanMyTiltPos">${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}</span>`
else { else if(shade.shadeType !== 5) {
divCtl += `<span class="shadectl-mypos"><label>My: </label><span id="spanMyPos">${shade.myPos > 0 ? shade.myPos + '%' : '---'}</span>`; divCtl += `<span class="shadectl-mypos"><label>My: </label><span id="spanMyPos">${shade.myPos > 0 ? shade.myPos + '%' : '---'}</span>`;
if (shade.myTiltPos > 0 && shade.tiltType !== 3) divCtl += `<label> Tilt: </label><span id="spanMyTiltPos">${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}</span>`; if (shade.myTiltPos > 0 && shade.tiltType !== 3) divCtl += `<label> Tilt: </label><span id="spanMyTiltPos">${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}</span>`;
} }
divCtl += '</div>'; divCtl += '</div>';
divCtl += `<div class="shadectl-buttons" data-shadeType="${shade.shadeType}">`;
divCtl += `<div class="shadectl-buttons">`;
divCtl += `<div class="button-sunflag cmd-button" data-cmd="sunflag" data-shadeid="${shade.shadeId}" data-on="${shade.flags & 0x01 ? 'true' : 'false'}" style="${!shade.sunSensor ? 'display:none' : ''}"><i class="icss-sun-c"></i><i class="icss-sun-o"></i></div>`; divCtl += `<div class="button-sunflag cmd-button" data-cmd="sunflag" data-shadeid="${shade.shadeId}" data-on="${shade.flags & 0x01 ? 'true' : 'false'}" style="${!shade.sunSensor ? 'display:none' : ''}"><i class="icss-sun-c"></i><i class="icss-sun-o"></i></div>`;
divCtl += `<div class="button-outline cmd-button" data-cmd="up" data-shadeid="${shade.shadeId}"><i class="icss-somfy-up"></i></div>`; divCtl += `<div class="button-outline cmd-button" data-cmd="up" data-shadeid="${shade.shadeId}"><i class="icss-somfy-up"></i></div>`;
divCtl += `<div class="button-outline cmd-button my-button" data-cmd="my" data-shadeid="${shade.shadeId}" style="font-size:2em;padding:10px;"><span>my</span></div>`; divCtl += `<div class="button-outline cmd-button my-button" data-cmd="my" data-shadeid="${shade.shadeId}" style="font-size:2em;padding:10px;"><span>my</span></div>`;
divCtl += `<div class="button-outline cmd-button" data-cmd="down" data-shadeid="${shade.shadeId}"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>`; divCtl += `<div class="button-outline cmd-button" data-cmd="down" data-shadeid="${shade.shadeId}"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>`;
divCtl += `<div class="button-outline cmd-button toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="toggle" data-shadeid="${shade.shadeId}"><i class="icss-somfy-toggle" style="margin-top:-4px;"></i></div>`;
divCtl += '</div></div>'; divCtl += '</div></div>';
divCtl += '</div>';
} }
document.getElementById('divShadeList').innerHTML = divCfg; document.getElementById('divShadeList').innerHTML = divCfg;
let shadeControls = document.getElementById('divShadeControls'); let shadeControls = document.getElementById('divShadeControls');
@ -2414,20 +2419,26 @@ class Somfy {
onShadeTypeChanged(el) { onShadeTypeChanged(el) {
let sel = document.getElementById('selShadeType'); let sel = document.getElementById('selShadeType');
let tilt = parseInt(document.getElementById('selTiltType').value, 10); let tilt = parseInt(document.getElementById('selTiltType').value, 10);
let sun = true;
let ico = document.getElementById('icoShade'); let ico = document.getElementById('icoShade');
switch (parseInt(sel.value, 10)) { let type = parseInt(sel.value, 10);
document.getElementById('somfyShade').setAttribute('data-shadetype', type);
document.getElementById('divSomfyButtons').setAttribute('data-shadetype', type);
switch (type) {
case 1: case 1:
document.getElementById('divTiltSettings').style.display = ''; document.getElementById('divTiltSettings').style.display = '';
if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade');
if (ico.classList.contains('icss-awning')) ico.classList.remove('icss-awning'); 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-shutter')) ico.classList.remove('icss-shutter');
if (!ico.classList.contains('icss-window-blind')) ico.classList.add('icss-window-blind'); if (!ico.classList.contains('icss-window-blind')) ico.classList.add('icss-window-blind');
if (ico.classList.contains('icss-garage')) ico.classList.remove('icss-garage');
break; break;
case 3: case 3:
document.getElementById('divTiltSettings').style.display = 'none'; document.getElementById('divTiltSettings').style.display = 'none';
if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade');
if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); 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-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-awning')) ico.classList.add('icss-awning');
tilt = false; tilt = false;
break; break;
@ -2436,14 +2447,26 @@ class Somfy {
if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade'); if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade');
if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); 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-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-shutter')) ico.classList.add('icss-shutter');
tilt = false; tilt = false;
break; break;
case 6:
case 5:
document.getElementById('divTiltSettings').style.display = 'none';
if (ico.classList.contains('icss-window-shade')) ico.classList.remove('icss-window-shade');
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');
sun = false;
tilt = false;
break;
default: default:
if (ico.classList.contains('icss-window-blind')) ico.classList.remove('icss-window-blind'); 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-awning')) ico.classList.remove('icss-awning');
if (!ico.classList.contains('icss-window-shade')) ico.classList.add('icss-window-shade'); 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-shutter')) ico.classList.remove('icss-shutter');
document.getElementById('divTiltSettings').style.display = 'none'; document.getElementById('divTiltSettings').style.display = 'none';
tilt = false; tilt = false;
@ -2452,9 +2475,11 @@ class Somfy {
document.getElementById('fldTiltTime').parentElement.style.display = tilt ? 'inline-block' : 'none'; document.getElementById('fldTiltTime').parentElement.style.display = tilt ? 'inline-block' : 'none';
document.getElementById('divLiftSettings').style.display = tilt === 3 ? 'none' : ''; document.getElementById('divLiftSettings').style.display = tilt === 3 ? 'none' : '';
document.querySelector('#divSomfyButtons i.icss-window-tilt').style.display = tilt ? '' : 'none'; document.querySelector('#divSomfyButtons i.icss-window-tilt').style.display = tilt ? '' : 'none';
document.getElementById('divSunSensor').style.display = sun ? '' : 'none';
} }
onShadeBitLengthChanged(el) { onShadeBitLengthChanged(el) {
document.getElementById('divStepSettings').style.display = parseInt(el.value, 10) === 80 ? '' : 'none'; document.getElementById('somfyShade').setAttribute('data-bitlength', el.value);
//document.getElementById('divStepSettings').style.display = parseInt(el.value, 10) === 80 ? '' : 'none';
} }
openEditShade(shadeId) { openEditShade(shadeId) {
if (typeof shadeId === 'undefined') { if (typeof shadeId === 'undefined') {
@ -2506,6 +2531,9 @@ class Somfy {
this.onShadeTypeChanged(document.getElementById('selShadeType')); this.onShadeTypeChanged(document.getElementById('selShadeType'));
let ico = document.getElementById('icoShade'); let ico = document.getElementById('icoShade');
switch (shade.shadeType) { switch (shade.shadeType) {
case 0:
document.getElementById('divSunSensor').style.display = '';
break;
case 1: case 1:
ico.classList.remove('icss-window-shade'); ico.classList.remove('icss-window-shade');
ico.classList.add('icss-window-blind'); ico.classList.add('icss-window-blind');
@ -2513,11 +2541,19 @@ class Somfy {
case 3: case 3:
ico.classList.remove('icss-window-shade'); ico.classList.remove('icss-window-shade');
ico.classList.add('icss-awning'); ico.classList.add('icss-awning');
document.getElementById('divSunSensor').style.display = '';
break; break;
case 4: case 4:
ico.classList.remove('icss-window-shade'); ico.classList.remove('icss-window-shade');
ico.classList.add('icss-shutter'); ico.classList.add('icss-shutter');
document.getElementById('divSunSensor').style.display = '';
break; break;
case 5:
ico.classList.remove('icss-window-shade');
ico.classList.add('icss-garage');
document.getElementById('divSunSensor').style.display = 'none';
break;
} }
let tilt = ico.parentElement.querySelector('i.icss-window-tilt'); let tilt = ico.parentElement.querySelector('i.icss-window-tilt');
tilt.style.display = shade.tiltType !== 0 ? '' : 'none'; tilt.style.display = shade.tiltType !== 0 ? '' : 'none';
@ -2896,22 +2932,39 @@ class Somfy {
}); });
} }
pairShade(shadeId) { pairShade(shadeId) {
let shadeType = parseInt(document.getElementById('somfyShade').getAttribute('data-shadetype'), 10);
let div = document.createElement('div'); let div = document.createElement('div');
let html = `<div id="divPairing" class="instructions" data-type="link-remote" data-shadeid="${shadeId}">`; let html = `<div id="divPairing" class="instructions" data-type="link-remote" data-shadeid="${shadeId}">`;
html += '<div>Follow the instructions below to pair this shade with a Somfy motor</div>'; if (shadeType === 5 || shadeType === 6) {
html += '<hr style="width:100%;margin:0px;"></hr>'; html += '<div>Follow the instructions below to pair ESPSomfy RTS with an RTS Garage Door motor</div>';
html += '<ul style="width:100%;margin:0px;padding-left:20px;font-size:14px;">'; html += '<hr style="width:100%;margin:0px;"></hr>';
html += '<li>Open the shade memory using an existing remote by pressing the prog button on the back until the shade jogs.</li>'; html += '<ul style="width:100%;margin:0px;padding-left:20px;font-size:14px;">';
html += '<li>After the shade jogs press the Prog button below</li>'; html += '<li>Open the garage door motor memory per instructions for your motor.</li>';
html += '<li>The shade should jog again indicating that the shade is paired. NOTE: On some motors you may need to press and hold the Prog button.</li>'; html += '<li>Once the memory is opened, press the prog button below</li>';
html += '<li>If the shade jogs, you can press the shade paired button.</li>'; html += '<li>For single button control ESPSomfy RTS will send a toggle command but for a 3 button control it will send a prog command.</li>';
html += '<li>If the shade does not jog, try pressing the prog button again.</li>'; html += '</ul>';
html += '</ul>'; html += `<div class="button-container">`;
html += `<div class="button-container">`; html += `<button id="btnSendPairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;">Prog</button>`;
html += `<button id="btnSendPairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;">Prog</button>`; html += `<button id="btnMarkPaired" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;" onclick="somfy.setPaired(${shadeId}, true);">Door Paired</button>`;
html += `<button id="btnMarkPaired" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;" onclick="somfy.setPaired(${shadeId}, true);">Shade Paired</button>`; html += `<button id="btnStopPairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block" >Close</button>`;
html += `<button id="btnStopPairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block" >Close</button>`; html += `</div>`;
html += `</div>`; }
else {
html += '<div>Follow the instructions below to pair this shade with a Somfy motor</div>';
html += '<hr style="width:100%;margin:0px;"></hr>';
html += '<ul style="width:100%;margin:0px;padding-left:20px;font-size:14px;">';
html += '<li>Open the shade memory using an existing remote by pressing the prog button on the back until the shade jogs.</li>';
html += '<li>After the shade jogs press the Prog button below</li>';
html += '<li>The shade should jog again indicating that the shade is paired. NOTE: On some motors you may need to press and hold the Prog button.</li>';
html += '<li>If the shade jogs, you can press the shade paired button.</li>';
html += '<li>If the shade does not jog, try pressing the prog button again.</li>';
html += '</ul>';
html += `<div class="button-container">`;
html += `<button id="btnSendPairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;">Prog</button>`;
html += `<button id="btnMarkPaired" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;" onclick="somfy.setPaired(${shadeId}, true);">Shade Paired</button>`;
html += `<button id="btnStopPairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block" >Close</button>`;
html += `</div>`;
}
let fnRepeatProg = (err, shade) => { let fnRepeatProg = (err, shade) => {
if (this.btnTimer) { if (this.btnTimer) {
clearTimeout(this.btnTimer); clearTimeout(this.btnTimer);

View file

@ -664,16 +664,6 @@ div.wait-overlay > .lds-roller {
padding: 7px; padding: 7px;
cursor: pointer; cursor: pointer;
} }
#divSomfyButtons div.button-outline {
margin-top: -10px;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 0px;
display: inline-block;
padding: 7px;
cursor: pointer;
vertical-align: middle;
}
.shade-positioner { .shade-positioner {
position: absolute; position: absolute;

View file

@ -109,4 +109,35 @@
padding: 25px; padding: 25px;
position: relative; position: relative;
} }
#divSomfyButtons div.button-outline {
margin-top: -10px;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 0px;
padding: 7px;
cursor: pointer;
vertical-align: middle;
}
.shadectl-buttons div.button-outline {
display: inline-block;
}
.shadectl-buttons[data-shadetype="6"] > .cmd-button[data-cmd="sunflag"],
.shadectl-buttons[data-shadetype="5"] > .cmd-button[data-cmd="sunflag"],
.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"] {
display: none;
}
.shadectl-buttons[data-shadetype="0"] > .button-outline[data-cmd="toggle"],
.shadectl-buttons[data-shadetype="1"] > .button-outline[data-cmd="toggle"],
.shadectl-buttons[data-shadetype="2"] > .button-outline[data-cmd="toggle"],
.shadectl-buttons[data-shadetype="3"] > .button-outline[data-cmd="toggle"],
.shadectl-buttons[data-shadetype="4"] > .button-outline[data-cmd="toggle"],
.shadectl-buttons[data-shadetype="6"] > .button-outline[data-cmd="toggle"] {
display: none;
}
#somfyShade[data-bitlength="56"] #divStepSettings,
#somfyShade[data-shadetype="5"] #divStepSettings,
#somfyShade[data-shadetype="6"] #divStepSettings {
display:none;
}