diff --git a/ConfigSettings.h b/ConfigSettings.h index 83253ba..e88458b 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -3,7 +3,7 @@ #ifndef configsettings_h #define configsettings_h -#define FW_VERSION "v2.0.1" +#define FW_VERSION "v2.0.2" enum DeviceStatus { DS_OK = 0, DS_ERROR = 1, diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 2f694c5..b05f55f 100644 Binary files a/SomfyController.ino.esp32.bin and b/SomfyController.ino.esp32.bin differ diff --git a/SomfyController.littlefs.bin b/SomfyController.littlefs.bin index 5880f22..da4bc31 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/Web.cpp b/Web.cpp index 9b1be3d..2e2f28e 100644 --- a/Web.cpp +++ b/Web.cpp @@ -393,7 +393,7 @@ void Web::begin() { uint8_t repeat = 1; somfy_commands command = somfy_commands::My; if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (apiServer.hasArg("shadeId")) { + if (apiServer.hasArg("groupId")) { groupId = atoi(apiServer.arg("groupId").c_str()); if (apiServer.hasArg("command")) command = translateSomfyCommand(apiServer.arg("command")); if(apiServer.hasArg("repeat")) repeat = atoi(apiServer.arg("repeat").c_str()); @@ -1139,7 +1139,7 @@ void Web::begin() { somfy_commands command = somfy_commands::My; if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { if (server.hasArg("groupId")) { - groupId = atoi(server.arg("shadeId").c_str()); + groupId = atoi(server.arg("groupId").c_str()); if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command")); if(server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str()); } diff --git a/data/appversion b/data/appversion index 359a5b9..f93ea0c 100644 --- a/data/appversion +++ b/data/appversion @@ -1 +1 @@ -2.0.0 \ No newline at end of file +2.0.2 \ No newline at end of file diff --git a/data/index.html b/data/index.html index 697451d..4765938 100644 --- a/data/index.html +++ b/data/index.html @@ -3,11 +3,11 @@ - - - + + + - +
diff --git a/data/index.js b/data/index.js index b1ad926..167328f 100644 --- a/data/index.js +++ b/data/index.js @@ -13,9 +13,9 @@ Date.prototype.toJSON = function () { return `${this.getFullYear()}-${(this.getMonth() + 1).fmt('00')}-${this.getDate().fmt('00')}T${this.getHours().fmt('00')}:${this.getMinutes().fmt('00')}:${this.getSeconds().fmt('00')}.${this.getMilliseconds().fmt('000')}${sign}${tzHrs}${tzMin}`; }; Date.prototype.fmt = function (fmtMask, emptyMask) { - if (fmtMask.match(/[hHmt]/g) !== null) { if (this.isDateTimeEmpty()) return typeof (emptyMask) !== 'undefined' ? emptyMask : ''; } - if (fmtMask.match(/[Mdy]/g) !== null) { if (this.isDateEmpty()) return typeof (emptyMask) !== 'undefined' ? emptyMask : ''; } - let formatted = (typeof (fmtMask) !== 'undefined' && fmtMask !== null) ? fmtMask : 'MM-dd-yyyy HH:mm:ss'; + if (fmtMask.match(/[hHmt]/g) !== null) { if (this.isDateTimeEmpty()) return typeof emptyMask !== 'undefined' ? emptyMask : ''; } + if (fmtMask.match(/[Mdy]/g) !== null) { if (this.isDateEmpty()) return typeof emptyMask !== 'undefined' ? emptyMask : ''; } + let formatted = typeof fmtMask !== 'undefined' && fmtMask !== null ? fmtMask : 'MM-dd-yyyy HH:mm:ss'; let letters = 'dMyHhmst'.split(''); let temp = []; let count = 0; @@ -36,14 +36,14 @@ Date.prototype.fmt = function (fmtMask, emptyMask) { yyyy: year, H: this.getHours().toString(), HH: this.getHours().toString().padStart(2, '00'), - h: this.getHours() === 0 ? '12' : (this.getHours() > 12) ? Math.abs(this.getHours() - 12).toString() : this.getHours().toString(), - hh: this.getHours() === 0 ? '12' : (this.getHours() > 12) ? Math.abs(this.getHours() - 12).toString().padStart(2, '00') : this.getHours().toString().padStart(2, '00'), + h: this.getHours() === 0 ? '12' : this.getHours() > 12 ? Math.abs(this.getHours() - 12).toString() : this.getHours().toString(), + hh: this.getHours() === 0 ? '12' : this.getHours() > 12 ? Math.abs(this.getHours() - 12).toString().padStart(2, '00') : this.getHours().toString().padStart(2, '00'), m: this.getMinutes().toString(), mm: this.getMinutes().toString().padStart(2, '00'), s: this.getSeconds().toString(), ss: this.getSeconds().toString().padStart(2, '00'), - t: (this.getHours() < 12 || this.getHours() === 24) ? 'a' : 'p', - tt: (this.getHours() < 12 || this.getHours() === 24) ? 'am' : 'pm' + t: this.getHours() < 12 || this.getHours() === 24 ? 'a' : 'p', + tt: this.getHours() < 12 || this.getHours() === 24 ? 'am' : 'pm' }; for (let i = 0; i < letters.length; i++) { regexA = new RegExp('(' + letters[i] + '+)'); @@ -152,10 +152,10 @@ Number.prototype.fmt = function (format, empty) { var baseUrl = window.location.protocol === 'file:' ? 'http://ESPSomfyRTS' : ''; //var baseUrl = ''; function makeBool(val) { - if (typeof (val) === 'boolean') return val; - if (typeof (val) === 'undefined') return false; - if (typeof (val) === 'number') return val >= 1; - if (typeof (val) === 'string') { + if (typeof val === 'boolean') return val; + if (typeof val === 'undefined') return false; + if (typeof val === 'number') return val >= 1; + if (typeof val === 'string') { if (val === '') return false; switch (val.toLowerCase().trim()) { case 'on': @@ -735,7 +735,7 @@ class UIBinder { if (typeof val.getMonth === 'function') return val.getTime(); var tval = val.replace(/[^0-9\.\-]+/g, ''); return tval.indexOf('.') !== -1 ? parseFloat(tval) : parseInt(tval, 10); - }; + } _bindValue(obj, el, val, arrayRef) { var binding = el.getAttribute('data-bind'); var dataType = el.getAttribute('data-datatype'); @@ -746,7 +746,7 @@ class UIBinder { for (var i = 0; i < arr.length - 1; i++) { let s = arr[i]; if (typeof s === 'undefined' || s.length === 0) continue; - sRef += ('.' + s); + sRef += '.' + s; var ndx = s.lastIndexOf('['); if (ndx !== -1) { var v = s.substring(0, ndx); @@ -1038,7 +1038,7 @@ class UIBinder { } } } - isConfigOpen() { return (window.getComputedStyle(document.getElementById('divConfigPnl')).display !== 'none'); } + isConfigOpen() { return window.getComputedStyle(document.getElementById('divConfigPnl')).display !== 'none'; } setConfigPanel() { if (this.isConfigOpen()) return; let divCfg = document.getElementById('divConfigPnl'); @@ -1186,7 +1186,7 @@ var security = new Security(); class General { initialized = false; - appVersion = 'v2.0.1'; + appVersion = 'v2.0.2'; reloadApp = false; init() { if (this.initialized) return; @@ -1468,7 +1468,7 @@ class General { ui.errorMessage('Invalid Pin').querySelector('.sub-message').innerHTML = 'Pins must be exactly 4 alpha-numeric values in length. Please enter a complete pin.'; return; } - confirm = '

Please keep your PIN safe and above all remember it. The only way to recover a lost PIN is to completely reload the onboarding firmware which will wipe out your configuration.

Have you stored your PIN in a safe place?

' + confirm = '

Please keep your PIN safe and above all remember it. The only way to recover a lost PIN is to completely reload the onboarding firmware which will wipe out your configuration.

Have you stored your PIN in a safe place?

'; } else if (security.type === 2) { // Password if (sec.username.length === 0) { @@ -1497,7 +1497,7 @@ class General { ui.errorMessage('Passwords do not Match').querySelector('.sub-message').innerHTML = 'Please re-enter the password exactly as you typed it in the Re-enter Password field.'; return; } - confirm = '

Please keep your password safe and above all remember it. The only way to recover a password is to completely reload the onboarding firmware which will wipe out your configuration.

Have you stored your username and password in a safe place?

' + confirm = '

Please keep your password safe and above all remember it. The only way to recover a password is to completely reload the onboarding firmware which will wipe out your configuration.

Have you stored your username and password in a safe place?

'; } let prompt = ui.promptMessage('Confirm Security', () => { putJSONSync('/saveSecurity', sec, (err, objApiKey) => { @@ -1745,10 +1745,10 @@ class Wifi { html += `
${obj.ethernet.PWRPin === -1 ? 'None' : obj.ethernet.PWRPin}
`; html += `
${obj.ethernet.MDCPin}
`; html += `
${obj.ethernet.MDIOPin}
`; - html += '
' - html += `
` - html += `` - html += `` + html += '
'; + html += `
`; + html += ``; + html += ``; html += `
`; div.innerHTML = html; document.getElementById('divContainer').appendChild(div); @@ -1799,13 +1799,13 @@ class Wifi { let ssid = strength.ssid || strength.name; document.getElementById('spanNetworkSSID').innerHTML = !ssid || ssid === '' ? '-------------' : ssid; document.getElementById('spanNetworkChannel').innerHTML = isNaN(strength.channel) || strength.channel < 0 ? '--' : strength.channel; - let cssClass = 'waveStrength-' + ((isNaN(strength.strength) || strength > 0) ? -100 : this.calcWaveStrength(strength.strength)); + let cssClass = 'waveStrength-' + (isNaN(strength.strength) || strength > 0 ? -100 : this.calcWaveStrength(strength.strength)); let elWave = document.getElementById('divNetworkStrength').children[0]; if (typeof elWave !== 'undefined' && !elWave.classList.contains(cssClass)) { elWave.classList.remove('waveStrength-0', 'waveStrength-1', 'waveStrength-2', 'waveStrength-3', 'waveStrength-4'); elWave.classList.add(cssClass); } - document.getElementById('spanNetworkStrength').innerHTML = (isNaN(strength.strength) || strength.strength <= -100) ? '----' : strength.strength; + document.getElementById('spanNetworkStrength').innerHTML = isNaN(strength.strength) || strength.strength <= -100 ? '----' : strength.strength; } procEthernet(ethernet) { console.log(ethernet); @@ -1814,7 +1814,7 @@ class Wifi { document.getElementById('spanEthernetStatus').innerHTML = ethernet.connected ? 'Connected' : 'Disconnected'; document.getElementById('spanEthernetSpeed').innerHTML = !ethernet.connected ? '--------' : `${ethernet.speed}Mbps ${ethernet.fullduplex ? 'Full-duplex' : 'Half-duplex'}`; } -}; +} var wifi = new Wifi(); class Somfy { initialized = false; @@ -1858,7 +1858,7 @@ class Somfy { } saveRadio() { let valid = true; - let getIntValue = (fld) => { return parseInt(document.getElementById(fld).value, 10); } + let getIntValue = (fld) => { return parseInt(document.getElementById(fld).value, 10); }; let trans = ui.fromElement(document.getElementById('divTransceiverSettings')).transceiver; // Check to make sure we have a trans type. if (typeof trans.config.type === 'undefined' || trans.config.type === '' || trans.config.type === 'none') { @@ -1960,8 +1960,8 @@ class Somfy { divCtl += `
`; divCtl += `${shade.name}`; - divCtl += `${shade.myPos > 0 ? shade.myPos + '%' : '---'}` - if (shade.myTiltPos > 0) divCtl += `${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}` + divCtl += `${shade.myPos > 0 ? shade.myPos + '%' : '---'}`; + if (shade.myTiltPos > 0) divCtl += `${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}`; divCtl += '
'; divCtl += `
`; @@ -2073,7 +2073,7 @@ class Somfy { divCtl += `
`; divCtl += `
`; divCtl += `${group.name}`; - divCtl += `
` + divCtl += `
`; for (let j = 0; j < group.linkedShades.length; j++) { divCtl += ''; if (j !== 0) divCtl += ', '; @@ -2133,9 +2133,9 @@ class Somfy { html += '' + html += '
'; html += `
`; - html += '
' + html += '
'; html += ``; html += ``; html += `
`; @@ -2280,7 +2280,7 @@ class Somfy { procRemoteFrame(frame) { console.log(frame); document.getElementById('spanRssi').innerHTML = frame.rssi; - document.getElementById('spanFrameCount').innerHTML = parseInt(document.getElementById('spanFrameCount').innerHTML, 10) + 1 + document.getElementById('spanFrameCount').innerHTML = parseInt(document.getElementById('spanFrameCount').innerHTML, 10) + 1; let lnk = document.getElementById('divLinking'); if (lnk) { let obj = { @@ -2335,7 +2335,7 @@ class Somfy { if (i !== 0) output += ',\n'; output += this.JSONPretty(obj[i], indent); } - output += ']' + output += ']'; return output; } else { @@ -2803,14 +2803,14 @@ class Somfy { html += '
BEWARE ... WARNING ... DANGER
'; html += '
'; html += '

If this shade is already paired with a motor then changing the rolling code WILL cause it to stop working. Rolling codes are tied to the remote address and the Somfy motor expects these to be sequential.

'; - html += '

If you hesitated just a little bit do not press the red button. Green represents safety so press it, wipe the sweat from your brow, and go through the normal pairing process.' + html += '

If you hesitated just a little bit do not press the red button. Green represents safety so press it, wipe the sweat from your brow, and go through the normal pairing process.'; html += '

'; html += ``; html += ''; - html += '
' - html += `
` - html += `` - html += `` + html += '
'; + html += `
`; + html += ``; + html += ``; html += `
`; div.innerHTML = html; document.getElementById('somfyShade').appendChild(div); @@ -2848,20 +2848,20 @@ class Somfy { pairShade(shadeId) { let div = document.createElement('div'); let html = `
`; - html += '
Follow the instructions below to pair this shade with a Somfy motor
' + html += '
Follow the instructions below to pair this shade with a Somfy motor
'; html += '
'; html += '
    '; html += '
  • Open the shade memory using an existing remote
  • '; html += '
  • Press the prog button on the back of the remote until the shade jogs
  • '; html += '
  • After the shade jogs press the Prog button below
  • '; html += '
  • The shade should jog again indicating that the shade is paired
  • '; - html += '
  • If the shade jogs, you can press the shade paired button.
  • ' - html += '
  • If the shade does not jog, press the prog button again.
  • ' - html += '
' - html += `
` - html += `` - html += `` - html += `` + html += '
  • If the shade jogs, you can press the shade paired button.
  • '; + html += '
  • If the shade does not jog, press the prog button again.
  • '; + html += ''; + html += `
    `; + html += ``; + html += ``; + html += ``; html += `
    `; div.innerHTML = html; document.getElementById('somfyShade').appendChild(div); @@ -2870,20 +2870,20 @@ class Somfy { unpairShade(shadeId) { let div = document.createElement('div'); let html = `
    `; - html += '
    Follow the instructions below to unpair this shade from a Somfy motor
    ' + html += '
    Follow the instructions below to unpair this shade from a Somfy motor
    '; html += '
    '; html += '
      '; html += '
    • Open the shade memory using an existing remote
    • '; html += '
    • Press the prog button on the back of the remote until the shade jogs
    • '; html += '
    • After the shade jogs press the Prog button below
    • '; html += '
    • The shade should jog again indicating that the shade is unpaired
    • '; - html += '
    • If the shade jogs, you can press the shade unpaired button.
    • ' - html += '
    • If the shade does not jog, press the prog button again until the shade jogs.
    • ' - html += '
    ' - html += `
    ` - html += `` - html += `` - html += `` + html += '
  • If the shade jogs, you can press the shade unpaired button.
  • '; + html += '
  • If the shade does not jog, press the prog button again until the shade jogs.
  • '; + html += ''; + html += `
    `; + html += ``; + html += ``; + html += ``; html += `
    `; div.innerHTML = html; document.getElementById('somfyShade').appendChild(div); @@ -2936,9 +2936,9 @@ class Somfy { html += '
    '; html += '

    This wizard will walk you through the steps required to add shades into a group. Follow all instructions at each step until the shade is added to the group.

    '; html += '

    During this process the shade should jog exactly two times. The first time indicates that the motor memory has been enabled and the second time adds the group to the motor memory

    '; - html += '

    Each shade must be paired individually to the group. When you are ready to begin pairing your shade to the group press the NEXT button.


    ' + html += '

    Each shade must be paired individually to the group. When you are ready to begin pairing your shade to the group press the NEXT button.


    '; - html += '
    ' + html += '
    '; html += '
    '; html += '

    Choose a shade that you would like to include in this group. Once you have chosen the shade to include in the link press the NEXT button.

    '; @@ -2954,7 +2954,7 @@ class Somfy { html += '
    '; html += '
    '; html += '
    '; - html += '
    ' + html += '
    '; html += '
    '; html += '
    '; @@ -2963,13 +2963,13 @@ class Somfy { html += '
    '; html += '
    '; html += '
    '; - html += '
    ' + html += '
    '; html += '
    '; html += `
    `; - html += `
    ` + html += `
    `; html += '
    '; div.innerHTML = html; document.getElementById('divContainer').appendChild(div); @@ -3043,8 +3043,8 @@ class Somfy { html += '
    '; html += '

    This wizard will walk you through the steps required to remove a shade from a group. Follow all instructions at each step until the shade is removed from the group.

    '; html += '

    During this process the shade should jog exactly two times. The first time indicates that the motor memory has been enabled and the second time removes the group from the motor memory

    '; - html += '

    Each shade must be removed from the group individually. When you are ready to begin unpairing your shade from the group press the NEXT button to begin.


    ' - html += '
    ' + html += '

    Each shade must be removed from the group individually. When you are ready to begin unpairing your shade from the group press the NEXT button to begin.


    '; + html += '
    '; html += '
    '; html += '

    You must first open the memory for the shade by pressing the OPEN MEMORY button. The shade should jog to indicate the memory has been opened.

    '; @@ -3052,7 +3052,7 @@ class Somfy { html += '
    '; html += '
    '; html += '
    '; - html += '
    ' + html += '
    '; html += '
    '; html += '
    '; @@ -3061,10 +3061,10 @@ class Somfy { html += '
    '; html += '
    '; html += '
    '; - html += '
    ' + html += '
    '; html += '
    '; html += `
    `; - html += `
    ` + html += `
    `; html += '
    '; div.innerHTML = html; document.getElementById('divContainer').appendChild(div); @@ -3111,7 +3111,7 @@ class Somfy { else { console.log(group); console.log(shadeId); - let shade = group.linkedShades.find((x) => { return shadeId === x.shadeId; }) + let shade = group.linkedShades.find((x) => { return shadeId === x.shadeId; }); if (typeof shade !== 'undefined') { // Add in all the available shades. let grpName = div.querySelector('#divGroupName'); @@ -3265,7 +3265,7 @@ class MQTT { if (err) ui.serviceError(err); console.log(response); }); - }; + } } var mqtt = new MQTT(); class Firmware { @@ -3273,8 +3273,8 @@ class Firmware { init() { this.initialized = true; } isMobile() { let agt = navigator.userAgent.toLowerCase(); - return /Android|iPhone|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent) - }; + return /Android|iPhone|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent); + } backup() { var link = document.createElement('a'); link.href = '/backup'; @@ -3282,13 +3282,13 @@ class Firmware { document.body.appendChild(link); link.click(); link.remove(); - }; + } restore() { let div = this.createFileUploader('/restore'); let inst = div.querySelector('div[id=divInstText]'); inst.innerHTML = '
    Select a backup file that you would like to restore then press the Upload File button.
    '; document.getElementById('divContainer').appendChild(div); - }; + } createFileUploader(service) { let div = document.createElement('div'); div.setAttribute('id', 'divUploadFile'); @@ -3302,21 +3302,21 @@ class Firmware { html += ``; html += `
    `; html += ``; - html += `` - html += `
    ` - html += `` - html += `` + html += ``; + html += `
    `; + html += ``; + html += ``; html += `
    `; html += `
    `; div.innerHTML = html; return div; - }; + } updateFirmware() { let div = this.createFileUploader('/updateFirmware'); let inst = div.querySelector('div[id=divInstText]'); inst.innerHTML = '
    Select a binary file [SomfyController.ino.esp32.bin] containing the device firmware then press the Upload File button.
    '; document.getElementById('divContainer').appendChild(div); - }; + } updateApplication() { let div = this.createFileUploader('/updateApplication'); general.reloadApp = true; @@ -3330,7 +3330,7 @@ class Firmware { inst.innerHTML += '
    A backup file for your configuration will be downloaded to your browser. If the application update process fails please restore this file using the restore button
    '; document.getElementById('divContainer').appendChild(div); if(this.isMobile()) document.getElementById('btnBackupCfg').style.display = 'inline-block'; - }; + } async uploadFile(service, el) { let field = el.querySelector('input[type="file"]'); let filename = field.value; @@ -3386,7 +3386,7 @@ class Firmware { return; } else if (!filename.endsWith('.backup') || filename.indexOf('ESPSomfyRTS') === -1) { - ui.errorMessage(el, 'This file is not a valid backup file') + ui.errorMessage(el, 'This file is not a valid backup file'); return; } break; @@ -3432,7 +3432,7 @@ class Firmware { } }; xhr.send(formData); - }; + } } var firmware = new Firmware(); diff --git a/data/main.css b/data/main.css index 92575f3..728e052 100644 --- a/data/main.css +++ b/data/main.css @@ -594,8 +594,8 @@ div.wait-overlay > .lds-roller { vertical-align: middle; margin-top: .15em; color: darkslategray; - top:0px; - left:0px; + top: 0; + left: 0; } .progress-bar::before { content: "";