Added step size for tilt wheels

This commit is contained in:
Robert Strouse 2023-04-29 12:48:31 -07:00
parent 7425a46d1a
commit fb677dc239
8 changed files with 60 additions and 26 deletions

View file

@ -6,9 +6,9 @@
extern Preferences pref; extern Preferences pref;
#define SHADE_HDR_VER 5 #define SHADE_HDR_VER 6
#define SHADE_HDR_SIZE 16 #define SHADE_HDR_SIZE 16
#define SHADE_REC_SIZE 222 #define SHADE_REC_SIZE 228
bool ConfigFile::begin(const char* filename, bool readOnly) { bool ConfigFile::begin(const char* filename, bool readOnly) {
this->file = LittleFS.open(filename, readOnly ? "r" : "w"); this->file = LittleFS.open(filename, readOnly ? "r" : "w");
@ -292,6 +292,9 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
shade->upTime = this->readUInt32(shade->upTime); shade->upTime = this->readUInt32(shade->upTime);
shade->downTime = this->readUInt32(shade->downTime); shade->downTime = this->readUInt32(shade->downTime);
shade->tiltTime = this->readUInt32(shade->tiltTime); shade->tiltTime = this->readUInt32(shade->tiltTime);
if(this->header.version > 5) {
shade->stepSize = this->readUInt16(100);
}
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) { for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
SomfyLinkedRemote *rem = &shade->linkedRemotes[j]; SomfyLinkedRemote *rem = &shade->linkedRemotes[j];
rem->setRemoteAddress(this->readUInt32(0)); rem->setRemoteAddress(this->readUInt32(0));
@ -346,6 +349,7 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
this->writeUInt32(shade->upTime); this->writeUInt32(shade->upTime);
this->writeUInt32(shade->downTime); this->writeUInt32(shade->downTime);
this->writeUInt32(shade->tiltTime); this->writeUInt32(shade->tiltTime);
this->writeUInt16(shade->stepSize);
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) { for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
SomfyLinkedRemote *rem = &shade->linkedRemotes[j]; SomfyLinkedRemote *rem = &shade->linkedRemotes[j];
this->writeUInt32(rem->getRemoteAddress()); this->writeUInt32(rem->getRemoteAddress());

View file

@ -1030,10 +1030,12 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
// the motor must tilt in the direction first then move // the motor must tilt in the direction first then move
// so we have to calculate the target with this in mind. // so we have to calculate the target with this in mind.
if(this->tiltType == tilt_types::integrated && this->currentTiltPos > 0.0f) { if(this->tiltType == tilt_types::integrated && this->currentTiltPos > 0.0f) {
this->tiltTarget = max(0.0f, this->currentTiltPos - (100.0f/(static_cast<float>(this->tiltTime/100.0f)))); if(this->tiltTime == 0 || this->stepSize == 0) return;
this->tiltTarget = max(0.0f, this->currentTiltPos - (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize)))));
} }
else if(this->currentPos > 0.0f) { else if(this->currentPos > 0.0f) {
this->target = max(0.0f, this->currentPos - (100.0f/(static_cast<float>(this->upTime/100.0f)))); if(this->downTime == 0 || this->stepSize == 0) return;
this->target = max(0.0f, this->currentPos - (100.0f/(static_cast<float>(this->upTime/static_cast<float>(this->stepSize)))));
} }
break; break;
case somfy_commands::MyDown: case somfy_commands::MyDown:
@ -1045,10 +1047,12 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
// the motor must tilt in the direction first then move // the motor must tilt in the direction first then move
// so we have to calculate the target with this in mind. // so we have to calculate the target with this in mind.
if(this->tiltType == tilt_types::integrated && this->currentTiltPos < 100.0f) { if(this->tiltType == tilt_types::integrated && this->currentTiltPos < 100.0f) {
this->tiltTarget = min(100.0f, this->currentTiltPos + (100.0f/(static_cast<float>(this->tiltTime/100.0f)))); if(this->tiltTime == 0 || this->stepSize == 0) return;
this->tiltTarget = min(100.0f, this->currentTiltPos + (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize)))));
} }
else if(this->currentPos < 100.0f) { else if(this->currentPos < 100.0f) {
this->target = min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/100.0f)))); if(this->downTime == 0 || this->stepSize == 0) return;
this->target = min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize)))));
} }
break; break;
default: default:
@ -1297,6 +1301,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
if(obj.containsKey("downTime")) this->downTime = obj["downTime"]; if(obj.containsKey("downTime")) this->downTime = obj["downTime"];
if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]); if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
if(obj.containsKey("tiltTime")) this->tiltTime = obj["tiltTime"]; if(obj.containsKey("tiltTime")) this->tiltTime = obj["tiltTime"];
if(obj.containsKey("stepSize")) this->stepSize = obj["stepSize"];
if(obj.containsKey("hasTilt")) this->tiltType = static_cast<bool>(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor; if(obj.containsKey("hasTilt")) this->tiltType = static_cast<bool>(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor;
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
if(obj.containsKey("shadeType")) { if(obj.containsKey("shadeType")) {
@ -1358,6 +1363,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
obj["tiltPosition"] = static_cast<uint8_t>(floor(this->currentTiltPos)); obj["tiltPosition"] = static_cast<uint8_t>(floor(this->currentTiltPos));
obj["tiltDirection"] = this->tiltDirection; obj["tiltDirection"] = this->tiltDirection;
obj["tiltTime"] = this->tiltTime; obj["tiltTime"] = this->tiltTime;
obj["stepSize"] = this->stepSize;
obj["tiltTarget"] = static_cast<uint8_t>(floor(this->tiltTarget)); obj["tiltTarget"] = static_cast<uint8_t>(floor(this->tiltTarget));
obj["target"] = this->target; obj["target"] = this->target;
obj["myPos"] = static_cast<int8_t>(floor(this->myPos)); obj["myPos"] = static_cast<int8_t>(floor(this->myPos));

View file

@ -154,6 +154,7 @@ class SomfyShade : public SomfyRemote {
uint32_t upTime = 10000; uint32_t upTime = 10000;
uint32_t downTime = 10000; uint32_t downTime = 10000;
uint32_t tiltTime = 7000; uint32_t tiltTime = 7000;
uint16_t stepSize = 100;
bool save(); bool save();
bool isIdle(); bool isIdle();
void checkMovement(); void checkMovement();

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
1.5.1 1.5.2

View file

@ -3,10 +3,10 @@
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="main.css?v=1.5.1" type="text/css" /> <link rel="stylesheet" href="main.css?v=1.5.2b" type="text/css" />
<link rel="stylesheet" href="icons.css?v=1.5.1" type="text/css" /> <link rel="stylesheet" href="icons.css?v=1.5.2b" type="text/css" />
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/png" href="favicon.png" />
<script type="text/javascript" src="index.js?v=1.5.1"></script> <script type="text/javascript" src="index.js?v=1.5.2b"></script>
</head> </head>
<body> <body>
<div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;"> <div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;">
@ -220,13 +220,13 @@
<div class="field-group" style="width:127px;display:inline-block;margin-top:-20px;float:left;"> <div class="field-group" style="width:127px;display:inline-block;margin-top:-20px;float:left;">
<div class="field-group"> <div class="field-group">
<div class="field-group"> <div class="field-group">
<select id="selShadeBitLength" name="bitLength" style="width:100%;"> <select id="selShadeBitLength" name="bitLength" style="width:100%;" onchange="somfy.onShadeBitLengthChanged(this);">
<option value="56">56-BIT</option> <option value="56">56-BIT</option>
<option value="80">80-BIT</option> <option value="80">80-BIT</option>
</select> </select>
<label for="selShadeBitLength">Bit Length</label> <label for="selShadeBitLength">Bit Length</label>
</div> </div>
<div class="field-group"> <div class="field-group" style="margin-top:-10px;">
<select id="selShadeType" name="shadeType" style="width:100%;" onchange="somfy.onShadeTypeChanged(this);"> <select id="selShadeType" name="shadeType" style="width:100%;" onchange="somfy.onShadeTypeChanged(this);">
<option value="0">Roller Shade</option> <option value="0">Roller Shade</option>
<option value="1">Blind</option> <option value="1">Blind</option>
@ -234,25 +234,25 @@
</select> </select>
<label for="selShadeType">Type</label> <label for="selShadeType">Type</label>
</div> </div>
<div class="field-group"> <div class="field-group" style="margin-top:-15px;">
<input id="fldShadeAddress" name="shadeAddress" type="number" length=5 placeholder="Address" style="width:100%;text-align:right;"> <input id="fldShadeAddress" name="shadeAddress" type="number" length=5 placeholder="Address" style="width:100%;text-align:right;">
<label for="fldShadeAddress">Remote Address</label> <label for="fldShadeAddress">Remote Address</label>
</div> </div>
</div> </div>
</div> </div>
<div id="divSomfyButtons" style="float:right;margin-top:27px;position:relative"> <div id="divSomfyButtons" 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" 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" 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" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'down');"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>
</div> </div>
</div> </div>
<div class="field-group"> <div class="field-group" style="padding:0px;">
<input id="fldShadeName" name="shadeName" type="text" length=20 placeholder="Name"> <input id="fldShadeName" name="shadeName" type="text" length=20 placeholder="Name">
<label for="fldShadeName">Name</label> <label for="fldShadeName">Name</label>
</div> </div>
<div> <div style="margin-top:-10px;">
<div class="field-group" style="display:inline-block;max-width:127px;margin-right:17px;"> <div class="field-group" style="display:inline-block;max-width:127px;margin-right:17px;margin-top:-10px;">
<input id="fldShadeUpTime" name="shadeUpTime" type="number" length=5 placeholder="milliseconds" style="width:100%;text-align:right;" /> <input id="fldShadeUpTime" name="shadeUpTime" type="number" length=5 placeholder="milliseconds" style="width:100%;text-align:right;" />
<label for="fldShadeUpTime">Up Time (ms)</label> <label for="fldShadeUpTime">Up Time (ms)</label>
</div> </div>
@ -261,7 +261,7 @@
<label for="fldShadeDownTime">Down Time (ms)</label> <label for="fldShadeDownTime">Down Time (ms)</label>
</div> </div>
</div> </div>
<div id="divTiltSettings" style="display:none;"> <div id="divTiltSettings" style="display:none;margin-top:-10px;">
<div class="field-group" style="display:inline-block; margin-right:17px;width:127px;vertical-align:middle;"> <div class="field-group" style="display:inline-block; margin-right:17px;width:127px;vertical-align:middle;">
<select id="selTiltType" name="tiltType" style="width:100%;" onchange="somfy.onShadeTypeChanged(this);"> <select id="selTiltType" name="tiltType" style="width:100%;" onchange="somfy.onShadeTypeChanged(this);">
<option value="0">None</option> <option value="0">None</option>
@ -275,6 +275,18 @@
<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 class="field-group">
<input id="slidStepSize" name="stepSize" type="range" min="1" max="1000" step="1" style="width:100%;" oninput="somfy.stepSizeChanged(this);" />
<label for="slidStepSize" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;">
<span>Step Size (smaller)</span>
<span style="float:right;display:inline-block;margin-right:7px;">
<span id="spanStepSize" style="color:black;"></span><span>(larger)</span>
</span>
</label>
</div>
</div>
<div class="button-container" style="text-align:center;"> <div class="button-container" style="text-align:center;">
<button id="btnPairShade" type="button" onclick="somfy.pairShade(parseInt(document.getElementById('spanShadeId').innerText, 10));" style="display:inline-block;width:47%;"> <button id="btnPairShade" type="button" onclick="somfy.pairShade(parseInt(document.getElementById('spanShadeId').innerText, 10));" style="display:inline-block;width:47%;">
Pair Shade Pair Shade

View file

@ -4,7 +4,7 @@ document.oncontextmenu = (event) => {
else { else {
event.preventDefault(); event.stopPropagation(); return false; event.preventDefault(); event.stopPropagation(); return false;
} }
} };
Date.prototype.toJSON = function () { Date.prototype.toJSON = function () {
let tz = this.getTimezoneOffset(); let tz = this.getTimezoneOffset();
let sign = tz > 0 ? '-' : '+'; let sign = tz > 0 ? '-' : '+';
@ -378,7 +378,7 @@ async function reopenSocket() {
await initSockets(); await initSockets();
} }
class General { class General {
appVersion = 'v1.5.1'; appVersion = 'v1.5.2beta';
reloadApp = false; reloadApp = false;
async init() { async init() {
this.setAppVersion(); this.setAppVersion();
@ -1266,7 +1266,6 @@ class Somfy {
case 37: case 37:
case 38: case 38:
continue; continue;
break;
case 32: // We cannot use this pin with the mask for TX. case 32: // We cannot use this pin with the mask for TX.
case 33: case 33:
case 34: // Input only case 34: // Input only
@ -1309,7 +1308,6 @@ class Somfy {
span = divs[i].querySelector('#spanMyTiltPos'); span = divs[i].querySelector('#spanMyTiltPos');
if (span) span.innerHTML = typeof state.myTiltPos !== 'undefined' && state.myTiltPos >= 0 ? `${state.myTiltPos}%` : '---'; if (span) span.innerHTML = typeof state.myTiltPos !== 'undefined' && state.myTiltPos >= 0 ? `${state.myTiltPos}%` : '---';
} }
}; };
procRemoteFrame(frame) { procRemoteFrame(frame) {
console.log(frame); console.log(frame);
@ -1348,7 +1346,7 @@ class Somfy {
if (i !== 0) html += ','; if (i !== 0) html += ',';
html += `${frame.pulses[i]}`; html += `${frame.pulses[i]}`;
} }
html += '</div>' html += '</div>';
row.innerHTML = html; row.innerHTML = html;
frames.prepend(row); frames.prepend(row);
this.frames.push(frame); this.frames.push(frame);
@ -1357,7 +1355,7 @@ class Somfy {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
let output = '['; let output = '[';
for (let i = 0; i < obj.length; i++) { for (let i = 0; i < obj.length; i++) {
if (i != 0) output += ',\n'; if (i !== 0) output += ',\n';
output += this.JSONPretty(obj[i], indent); output += this.JSONPretty(obj[i], indent);
} }
output += ']' output += ']'
@ -1417,6 +1415,9 @@ class Somfy {
document.getElementById('fldTiltTime').parentElement.style.display = tilt ? 'inline-block' : 'none'; document.getElementById('fldTiltTime').parentElement.style.display = tilt ? 'inline-block' : 'none';
document.querySelector('#divSomfyButtons i.icss-window-tilt').style.display = tilt ? '' : 'none'; document.querySelector('#divSomfyButtons i.icss-window-tilt').style.display = tilt ? '' : 'none';
}; };
onShadeBitLengthChanged(el) {
document.getElementById('divStepSettings').style.display = parseInt(el.value, 10) === 80 ? '' : 'none';
}
openEditShade(shadeId) { openEditShade(shadeId) {
console.log('Opening Edit Shade'); console.log('Opening Edit Shade');
if (typeof shadeId === 'undefined') { if (typeof shadeId === 'undefined') {
@ -1443,6 +1444,8 @@ class Somfy {
document.getElementById('divLinkedRemoteList').innerHTML = ''; document.getElementById('divLinkedRemoteList').innerHTML = '';
document.getElementById('btnSetRollingCode').style.display = 'none'; document.getElementById('btnSetRollingCode').style.display = 'none';
document.getElementById('selShadeBitLength').value = shade.bitLength || 56; document.getElementById('selShadeBitLength').value = shade.bitLength || 56;
document.getElementById('slidStepSize').value = shade.stepSize || 100;
document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0');
} }
}); });
} }
@ -1471,6 +1474,8 @@ class Somfy {
document.getElementsByName('shadeName')[0].value = shade.name; document.getElementsByName('shadeName')[0].value = shade.name;
document.getElementsByName('shadeUpTime')[0].value = shade.upTime; document.getElementsByName('shadeUpTime')[0].value = shade.upTime;
document.getElementsByName('shadeDownTime')[0].value = shade.downTime; document.getElementsByName('shadeDownTime')[0].value = shade.downTime;
document.getElementById('slidStepSize').value = shade.stepSize;
document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0');
document.getElementById('fldTiltTime').value = shade.tiltTime; document.getElementById('fldTiltTime').value = shade.tiltTime;
document.getElementById('selTiltType').value = shade.tiltType; document.getElementById('selTiltType').value = shade.tiltType;
this.onShadeTypeChanged(document.getElementById('selShadeType')); this.onShadeTypeChanged(document.getElementById('selShadeType'));
@ -1488,6 +1493,7 @@ class Somfy {
ico.style.setProperty('--shade-position', `${shade.position}%`); ico.style.setProperty('--shade-position', `${shade.position}%`);
ico.style.setProperty('--tilt-position', `${shade.tiltPosition}%`); ico.style.setProperty('--tilt-position', `${shade.tiltPosition}%`);
ico.setAttribute('data-shadeid', shade.shadeId); ico.setAttribute('data-shadeid', shade.shadeId);
somfy.onShadeBitLengthChanged(document.getElementById('selShadeBitLength'));
document.getElementById('btnSetRollingCode').style.display = 'inline-block'; document.getElementById('btnSetRollingCode').style.display = 'inline-block';
if (shade.paired) { if (shade.paired) {
document.getElementById('btnUnpairShade').style.display = 'inline-block'; document.getElementById('btnUnpairShade').style.display = 'inline-block';
@ -1523,9 +1529,10 @@ class Somfy {
downTime: parseInt(document.getElementsByName('shadeDownTime')[0].value, 10), downTime: parseInt(document.getElementsByName('shadeDownTime')[0].value, 10),
shadeType: parseInt(document.getElementById('selShadeType').value, 10), shadeType: parseInt(document.getElementById('selShadeType').value, 10),
tiltTime: parseInt(document.getElementById('fldTiltTime').value, 10), tiltTime: parseInt(document.getElementById('fldTiltTime').value, 10),
bitLength: parseInt(document.getElementById('selShadeBitLength').value, 10) || 56 bitLength: parseInt(document.getElementById('selShadeBitLength').value, 10) || 56,
stepSize: parseInt(document.getElementById('slidStepSize').value, 10) || 100
}; };
if (obj.shadeType == 1) { if (obj.shadeType === 1) {
obj.tiltType = parseInt(document.getElementById('selTiltType').value, 10); obj.tiltType = parseInt(document.getElementById('selTiltType').value, 10);
} }
else obj.tiltType = 0; else obj.tiltType = 0;
@ -1857,6 +1864,10 @@ class Somfy {
let lvls = [-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12]; let lvls = [-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12];
document.getElementById('spanTxPower').innerText = lvls[el.value]; document.getElementById('spanTxPower').innerText = lvls[el.value];
}; };
stepSizeChanged(el) {
document.getElementById('spanStepSize').innerText = parseInt(el.value, 10).fmt('#,##0');
};
processShadeTarget(el, shadeId) { processShadeTarget(el, shadeId) {
let positioner = document.querySelector(`.shade-positioner[data-shadeid="${shadeId}"]`); let positioner = document.querySelector(`.shade-positioner[data-shadeid="${shadeId}"]`);
if (positioner) { if (positioner) {