Gate Control #199 Prog Hold for Groups #195

This commit is contained in:
Robert Strouse 2023-12-10 11:19:51 -08:00
parent d97f150439
commit 0c36c8e746
9 changed files with 398 additions and 257 deletions

View file

@ -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,

View file

@ -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<uint8_t>(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<uint8_t>(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<uint8_t>(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<const char *>(), "shutter", 8) == 0)
type = shade_types::shutter;
else if(strncmp(obj["shadeType"].as<const char *>(), "drycontact2", 12) == 0)
type = shade_types::drycontact2;
else if(strncmp(obj["shadeType"].as<const char *>(), "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<uint8_t>() : this->gpioDown;
uint8_t myPin = obj.containsKey("gpioMy") ? obj["gpioMy"].as<uint8_t>() : 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<const char *>(), "shutter", 8) == 0)
this->shadeType = shade_types::shutter;
else if(strncmp(obj["shadeType"].as<const char *>(), "drycontact2", 12) == 0)
this->shadeType = shade_types::drycontact2;
else if(strncmp(obj["shadeType"].as<const char *>(), "drycontact", 11) == 0)
this->shadeType = shade_types::drycontact;
}

View file

@ -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,

Binary file not shown.

Binary file not shown.

View file

@ -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;
}

View file

@ -3,11 +3,11 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="main.css?v=2.2.2b" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.2.2b" type="text/css" />
<link rel="stylesheet" href="icons.css?v=2.2.2b" type="text/css" />
<link rel="stylesheet" href="main.css?v=2.2.2c" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.2.2c" type="text/css" />
<link rel="stylesheet" href="icons.css?v=2.2.2c" type="text/css" />
<link rel="icon" type="image/png" href="favicon.png" />
<script type="text/javascript" src="index.js?v=2.2.2b"></script>
<script type="text/javascript" src="index.js?v=2.2.2c"></script>
</head>
<body>
<div id="divContainer" class="container main" data-auth="false">
@ -353,6 +353,10 @@
<option value="5">Garage (1-button)</option>
<option value="6">Garage (3-button)</option>
<option value="9">Dry Contact</option>
<option value="10">Dry Contact (2-button)</option>
<option value="11">Gate (left)</option>
<option value="12">Gate (center)</option>
<option value="13">Gate (right)</option>
</select>
<label for="selShadeType">Type</label>
</div>

View file

@ -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 += `<div class="somfyShade shade-draggable" draggable="true" data-mypos="${shade.myPos}" data-shadeid="${shade.shadeId}" data-remoteaddress="${shade.remoteAddress}" data-tilt="${shade.tiltType}" data-shadetype="${shade.shadeType}">`;
divCfg += `<div class="button-outline" onclick="somfy.openEditShade(${shade.shadeId});"><i class="icss-edit"></i></div>`;
//divCfg += `<i class="shade-icon" data-position="${shade.position || 0}%"></i>`;
@ -2112,38 +2130,10 @@ class Somfy {
divCtl += ` data-tiltposition="${shade.tiltPosition}" data-tiltdirection="${shade.tiltDirection}" data-tilttarget="${shade.tiltTarget}"`;
}
divCtl += `><div class="shade-icon" data-shadeid="${shade.shadeId}" onclick="event.stopPropagation(); console.log(event); somfy.openSetPosition(${shade.shadeId});">`;
divCtl += `<i class="somfy-shade-icon`;
switch (shade.shadeType) {
case 1:
divCtl += ' icss-window-blind';
break;
case 2:
divCtl += ' icss-ldrapery';
break;
case 3:
divCtl += ' icss-awning';
break;
case 4:
divCtl += ' icss-shutter';
break;
case 5:
case 6:
divCtl += ' icss-garage';
break;
case 7:
divCtl += ' icss-rdrapery';
break;
case 8:
divCtl += ' icss-cdrapery';
break;
case 9:
divCtl += ' icss-lightbulb';
break;
default:
divCtl += ' icss-window-shade';
break;
}
divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.flipPosition ? 100 - shade.position : shade.position}%;vertical-align: top;"><span class="icss-panel-left"></span><span class="icss-panel-right"></span></i>`;
divCtl += `<i class="somfy-shade-icon ${st.ico}`;
//divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.flipPosition ? 100 - shade.position : shade.position}%;vertical-align: top;"><span class="icss-panel-left"></span><span class="icss-panel-right"></span></i>`;
divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.position}%;vertical-align: top;"><span class="icss-panel-left"></span><span class="icss-panel-right"></span></i>`;
divCtl += shade.tiltType !== 0 ? `<i class="icss-window-tilt" data-shadeid="${shade.shadeId}" data-tiltposition="${shade.tiltPosition}"></i></div>` : '</div>';
divCtl += `<div class="indicator indicator-wind"><i class="icss-warning"></i></div><div class="indicator indicator-sun"><i class="icss-sun"></i></div>`;
divCtl += `<div class="shade-name">`;
@ -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 = `<hr></hr><p>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.</p></p><p>Once the shade has jogged the shade will be added to the group and this process will be finished.</p>`;
}
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 = `<hr></hr><p>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.</p></p><p>Once the shade has jogged the shade will be added to the group and this process will be finished.</p>`;
});
getJSONSync(`/groupOptions?groupId=${groupId}`, (err, options) => {
if (err) {

View file

@ -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:";
}